C# SynchronizationContext做什么?
在《编程C#》一书中,它有一些关于同步上下文的示例代码:C# SynchronizationContext做什么?,c#,.net,multithreading,C#,.net,Multithreading,在《编程C#》一书中,它有一些关于同步上下文的示例代码: SynchronizationContext originalContext = SynchronizationContext.Current; ThreadPool.QueueUserWorkItem(delegate { string text = File.ReadAllText(@"c:\temp\log.txt"); originalContext.Post(delegate { myTextBo
SynchronizationContext originalContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(delegate {
string text = File.ReadAllText(@"c:\temp\log.txt");
originalContext.Post(delegate {
myTextBox.Text = text;
}, null);
});
我是一个线程初学者,所以请详细回答。
首先,我不知道上下文是什么意思,程序在originalContext
中保存了什么?当启动Post
方法时,UI线程将做什么?如果我问一些愚蠢的问题,请纠正我,谢谢
编辑:例如,如果我只写
myTextBox.Text=Text
在该方法中,有什么区别?此处同步上下文的目的是确保myTextbox.Text=Text代码>在主UI线程上被调用
Windows要求GUI控件只能由创建它们时使用的线程访问。如果您尝试在后台线程中分配文本,而不首先进行同步(通过多种方式中的任何一种,例如,此方式或调用模式),则将引发异常
这样做的目的是在创建后台线程之前保存同步上下文,然后后台线程使用context.Post方法执行GUI代码
是的,您显示的代码基本上是无用的。为什么创建一个后台线程,只需立即返回到主UI线程?这只是一个示例。它存储同步提供程序,一个从SynchronizationContext派生的类。在这种情况下,它可能是WindowsFormsSynchronizationContext的一个实例。该类使用Control.Invoke()和Control.BeginInvoke()方法来实现Send()和Post()方法。也可以是DispatcherSynchronizationContext,它使用Dispatcher.Invoke()和BeginInvoke()。在Winforms或WPF应用程序中,该提供程序将在您创建窗口时自动安装
当您在另一个线程上运行代码时,比如代码段中使用的线程池线程,那么您必须小心不要直接使用线程不安全的对象。与任何用户界面对象一样,必须从创建TextBox的线程更新TextBox.Text属性。Post()方法确保委托目标在该线程上运行
请注意,此代码段有点危险,它只有在从UI线程调用时才能正常工作。SynchronizationContext.Current在不同线程中具有不同的值。只有UI线程具有可用值。这也是代码不得不复制它的原因。在Winforms应用程序中,一种更可读、更安全的方法:
ThreadPool.QueueUserWorkItem(delegate {
string text = File.ReadAllText(@"c:\temp\log.txt");
myTextBox.BeginInvoke(new Action(() => {
myTextBox.Text = text;
}));
});
它的优点是,当从任何线程调用时,它都可以工作。使用SynchronizationContext.Current的优点是,无论代码是在Winforms中使用还是在WPF中使用,它都可以工作,在库中很重要。这当然不是这类代码的一个很好的例子,你总是知道这里有什么类型的文本框,所以你总是知道是使用Control.BeginInvoke还是Dispatcher.BeginInvoke。实际上使用SynchronizationContext.Current并不常见
这本书试图教你关于线程的知识,所以使用这个有缺陷的例子是很好的。在现实生活中,在少数情况下,您可能会考虑使用SimultIsActualField.NoW,但您仍然将其保留到C的异步/等待关键字或TaskStjult.FasCurrnTraceSimultCutExter()来为您做这件事。但是请注意,当您在错误的线程上使用代码片段时,它们仍然会像代码片段那样行为不端,原因完全相同。这是一个非常常见的问题,额外的抽象级别很有用,但更难找出它们为什么不能正确工作。希望这本书也能告诉你什么时候不用:)
SynchronizationContext做什么
简单地说,表示可能执行代码的“位置”。传递给其或的委托随后将在该位置调用。(Post
是Send
的非阻塞/异步版本)
每个线程都可以有一个与其关联的SynchronizationContext
实例。运行线程可以通过调用与同步上下文相关联,并且可以通过查询运行线程的当前上下文
不管我刚才写了什么(每个线程都有一个关联的同步上下文),SynchronizationContext
不一定代表一个特定的线程;它还可以将传递给它的委托调用转发给多个线程中的任何一个(例如,工作线程),或者(至少在理论上)转发给特定的CPU核心,甚至转发给另一个网络主机。代理最终运行的位置取决于使用的SynchronizationContext
类型
Windows窗体将在创建第一个窗体的线程上安装WindowsFormsSynchronizationContext
。(此线程通常称为“UI线程”。)这种类型的同步上下文调用在该线程上传递给它的委托。这非常有用,因为Windows窗体与许多其他UI框架一样,只允许在创建控件的同一线程上操作控件
如果我只写myTextBox.Text=Text代码>在方法中,有什么区别
传递给的代码将在线程池工作线程上运行。也就是说,它不会在创建myTextBox
的线程上执行,因此Windows窗体迟早会(尤其是在发布版本中)抛出异常,告诉您可能无法从另一个线程访问myTextBox
这就是为什么在特定分配之前,您必须以某种方式从工作线程“切换回”到“UI线程”(其中创建了myTextBox
)。具体做法如下:
当您仍在UI线程上时,在此处捕获Windows窗体的同步上下文,并将对它的引用存储在变量(originalContext
)中以供以后使用。您必须查询Synchro
void WaitForTwoSecondsAsync (Action continuation)
{
continuation.Dump();
var syncContext = AsyncOperationManager.SynchronizationContext;
new Timer (_ => syncContext.Post (o => continuation(), _)).Change (2000, -1);
}
void Main()
{
Util.CreateSynchronizationContext();
("Waiting on thread " + Thread.CurrentThread.ManagedThreadId).Dump();
for (int i = 0; i < 10; i++)
WaitForTwoSecondsAsync (() => ("Done on thread " + Thread.CurrentThread.ManagedThreadId).Dump());
}
private void SynchronizationContext SyncContext = SynchronizationContext.Current;
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(Work1);
thread.Start(SyncContext);
}
private void Work1(object state)
{
SynchronizationContext syncContext = state as SynchronizationContext;
syncContext.Post(UpdateTextBox, syncContext);
}
private void UpdateTextBox(object state)
{
Thread.Sleep(1000);
string text = File.ReadAllText(@"c:\temp\log.txt");
myTextBox.Text = text;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Thread.Sleep(1000);
string text = File.ReadAllText(@"c:\temp\log.txt");
myTextBox.Text = text;
}