Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/258.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# SynchronizationContext做什么?_C#_.net_Multithreading - Fatal编程技术网

C# SynchronizationContext做什么?

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

在《编程C#》一书中,它有一些关于同步上下文的示例代码:

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;
    }