ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

What does SynchronizationContext do?

2022-07-11 11:34:40  阅读:186  来源: 互联网

标签:do What code thread Windows SynchronizationContext does UI context


What does SynchronizationContext do?

问题

In the book Programming C#, it has some sample code about SynchronizationContext:

SynchronizationContext originalContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(delegate {
    string text = File.ReadAllText(@"c:\temp\log.txt");
    originalContext.Post(delegate {
        myTextBox.Text = text;
    }, null);
});

I'm a beginner in threads, so please answer in detail. First, I don't know what does context mean, what does the program save in the originalContext? And when the Post method is fired, what will the UI thread do?
If I ask some silly things, please correct me, thanks!

EDIT: For example, what if I just write myTextBox.Text = text; in the method, what's the difference?

 

评论

@RoyiNamir: Yes, but guess what: async/await relies on SynchronizationContext underneath. – stakx - no longer contributing Aug 7, 2013 at 8:55  

回答1

What does SynchronizationContext do?

Simply put, SynchronizationContext represents a location "where" code might be executed. Delegates that are passed to its Send or Post method will then be invoked in that location. (Post is the non-blocking / asynchronous version of Send.)

Every thread can have a SynchronizationContext instance associated with it. The running thread can be associated with a synchronization context by calling the static SynchronizationContext.SetSynchronizationContext method, and the current context of the running thread can be queried via the SynchronizationContext.Current property.

Despite what I just wrote (each thread having an associated synchronization context), a SynchronizationContext does not necessarily represent a specific thread; it can also forward invocation of the delegates passed to it to any of several threads (e.g. to a ThreadPool worker thread), or (at least in theory) to a specific CPU core, or even to another network host. Where your delegates end up running is dependent on the type of SynchronizationContext used.

Windows Forms will install a WindowsFormsSynchronizationContext on the thread on which the first form is created. (This thread is commonly called "the UI thread".) This type of synchronization context invokes the delegates passed to it on exactly that thread. This is very useful since Windows Forms, like many other UI frameworks, only permits manipulation of controls on the same thread on which they were created.

What if I just write myTextBox.Text = text; in the method, what's the difference?

The code that you've passed to ThreadPool.QueueUserWorkItem will be run on a thread pool worker thread. That is, it will not execute on the thread on which your myTextBox was created, so Windows Forms will sooner or later (especially in Release builds) throw an exception, telling you that you may not access myTextBox from across another thread.

This is why you have to somehow "switch back" from the worker thread to the "UI thread" (where myTextBox was created) before that particular assignment. This is done as follows:

  1. While you are still on the UI thread, capture Windows Forms' SynchronizationContext there, and store a reference to it in a variable (originalContext) for later use. You must query SynchronizationContext.Current at this point; if you queried it inside the code passed to ThreadPool.QueueUserWorkItem, you might get whatever synchronization context is associated with the thread pool's worker thread. Once you have stored a reference to Windows Forms' context, you can use it anywhere and at any time to "send" code to the UI thread.

  2. Whenever you need to manipulate a UI element (but are not, or might not be, on the UI thread anymore), access Windows Forms' synchronization context via originalContext, and hand off the code that will manipulate the UI to either Send or Post.


Final remarks and hints:

  • What synchronization contexts won't do for you is telling you which code must run in a specific location / context, and which code can just be executed normally, without passing it to a SynchronizationContext. In order to decide that, you must know the rules and requirements of the framework you're programming against — Windows Forms in this case.

    So remember this simple rule for Windows Forms: DO NOT access controls or forms from a thread other than the one that created them. If you must do this, use the SynchronizationContext mechanism as described above, or Control.BeginInvoke (which is a Windows Forms-specific way of doing exactly the same thing).

  • If you're programming against .NET 4.5 or later, you can make your life much easier by converting your code that explicitly uses SynchronizationContext, ThreadPool.QueueUserWorkItem, control.BeginInvoke, etc. over to the new async / await keywords and the Task Parallel Library (TPL), i.e. the API surrounding the Task and Task<TResult> classes. These will, to a very high degree, take care of capturing the UI thread's synchronization context, starting an asynchronous operation, then getting back onto the UI thread so you can process the operation's result.

 

评论

@user34660: No, that is not correct. You can have several threads that create Windows Forms controls. But each control is associated with the one thread that created it, and must only be accessed by that one thread. Controls from different UI threads are also very limited in how they interact with each other: one cannot be the parent/child of the other, data binding between them isn't possible, etc. Finally, each thread that creates controls needs its own message loop (which gets started by Application.Run, IIRC). This is a fairly advanced topic and not something casually done. – stakx - no longer contributing Jan 24, 2017 at 8:55

 

回答2

I'd like to add to other answers, SynchronizationContext.Post just queues a callback for later execution on the target thread (normally during the next cycle of the target thread's message loop), and then execution continues on the calling thread. On the other hand, SynchronizationContext.Send tries to execute the callback on the target thread immediately, which blocks the calling thread and may result in deadlock. In both cases, there is a possibility for code reentrancy (entering a class method on the same thread of execution before the previous call to the same method has returned).

If you're familiar with Win32 programming model, a very close analogy would be PostMessage and SendMessage APIs, which you can call to dispatch a message from a thread different from the target window's one.

Here is a very good explanation of what synchronization contexts are: It's All About the SynchronizationContext.

 

回答3

The purpose of the synchronization context here is to make sure that myTextbox.Text = text; gets called on the main UI thread.

Windows requires that GUI controls be accessed only by the thread they were created with. If you try assign the text in a background thread without first synchronizing (through any of several means, such as this or the Invoke pattern) then an exception will be thrown.

What this does is save the synchronization context prior to creating the background thread, then the background thread uses the context.Post method execute the GUI code.

Yes, the code you've shown is basically useless. Why create a background thread, only to immediately need to go back to the main UI thread? It's just an example.

 

 

 

标签:do,What,code,thread,Windows,SynchronizationContext,does,UI,context
来源: https://www.cnblogs.com/chucklu/p/16465807.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有