为什么 SynchronizationContext 不能正常工作?
我有以下代码:
[TestMethod]
public void StartWorkInFirstThread()
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Start work in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var action = ((Action) DoSomethingInSecondThread);
action.BeginInvoke(CallbackInSecondThread, syncContext);
// Continue its own work
}
private static void DoSomethingInSecondThread()
{
Console.WriteLine("Do something in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
private void CallbackInSecondThread(IAsyncResult ar)
{
Console.WriteLine("Callback in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var syncContext = (SynchronizationContext) ar.AsyncState;
syncContext.Post(CallbackInFirstThread, null);
}
private void CallbackInFirstThread(object obj)
{
Console.WriteLine("Callback in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
我希望最后一个方法在第一个线程中执行,即从中获取 SynchronizationContext 的初始线程,因为我调用此上下文的 Post()
方法。即是这样的:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (28)
这不就是SynchronizationContext的意义吗?但实际上我有以下输出:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (7)
问题是什么? SynchronizationContext 是否出了问题或者我有一些误解?
更新: 我将此方法称为使用 Resharper 测试运行程序的单元测试。
I have following code:
[TestMethod]
public void StartWorkInFirstThread()
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Start work in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var action = ((Action) DoSomethingInSecondThread);
action.BeginInvoke(CallbackInSecondThread, syncContext);
// Continue its own work
}
private static void DoSomethingInSecondThread()
{
Console.WriteLine("Do something in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
private void CallbackInSecondThread(IAsyncResult ar)
{
Console.WriteLine("Callback in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var syncContext = (SynchronizationContext) ar.AsyncState;
syncContext.Post(CallbackInFirstThread, null);
}
private void CallbackInFirstThread(object obj)
{
Console.WriteLine("Callback in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
I expect last method to be executed in the first thread, i.e. initial thread where SynchronizationContext is taken from, because I call Post()
method of this context. I.e. something like this:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (28)
Isn't it the meaning of SynchronizationContext? But actually I have following output:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (7)
What is the problem? Does something go wrong with SynchronizationContext or I have some misunderstanding?
Update: I call this method as a unit test using Resharper test runner.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
请参阅http://www.codeproject.com/KB/threads/SynchronizationContext.aspx
这就是您需要的答案。您必须重写
SynchronizationContext
才能使其正确处理您的操作。阅读开始于:
See http://www.codeproject.com/KB/threads/SynchronizationContext.aspx
There is the answer you need. You must override
SynchronizationContext
to make it properly handling your operations.Read starting from:
SynchronizationContext 的默认实现仅在调用中执行传递的委托线程(在调用 Send/Post 方法的线程中,而不是在捕获上下文的线程中)。如果您需要一些特定的行为,例如某些操作的线程关联性,您应该手动实现它。 BCL 包含一些用于简化 UI 互操作性的现成实现,例如 WindowsFormsSynchronizationContext 或 DispatcherSynchronizationContext。
Default implementation of SynchronizationContext just executes passed delegate in the calling thread (in the thread that invokes Send/Post method not the thread that captures context). If you need some particular behavior, like thread affinity for some operations, you should implement this manually. BCL contains few out-of-box implementations for simplification of UI interoperability, like WindowsFormsSynchronizationContext or DispatcherSynchronizationContext.
您的期望是错误的,因为没有通用的方法将委托“注入”到正在运行的线程中。您的“第一个线程”在测试运行器中启动,将执行一个或多个测试,然后停止 - 无法中断它并告诉它运行 CallbackInFirstThread 。
SynchronizationContext
类在线程池中运行Post
ed 委托,因为这是它唯一的选项。WindowsFormsSynchronizationContext
等派生类利用 WinForms 应用程序中的消息循环将Post
委托传递给 UI 线程,但测试运行程序中没有等效的方法。如果您想检查您正在测试的代码正在使用哪个
SynchronizationContext
,您可以创建自己的派生类,该类设置一个您可以在测试中检查的标志。下面是一个示例:在
StartWorkInFirstThread
中,将上下文设置为TestSynchronizationContext
的实例:调用
BeginInvoke
后,需要等待>Post
在退出测试之前发生,因此调用:在
CallbackInFirstThread
中,您可以检查正在使用的上下文,例如:关键是没有简单的方法来实际回发到第一个线程,但您可以检查是否使用了正确的上下文,以便当您的代码在实际应用程序中运行时,回调将在 UI 线程中运行。
Your expectation is wrong because there's no general way to "inject" a delegate into a running thread. Your "first thread" was started in the test runner, will execute one or more tests, and will then stop - there's no way to interrupt it and tell it to run
CallbackInFirstThread
. TheSynchronizationContext
class runsPost
-ed delegates in the thread pool because that's about the only option it has.Derived classes like
WindowsFormsSynchronizationContext
make use of the message loop in WinForms applications to pass thePost
-ed delegate to the UI thread, but there's no equivalent in a test runner.If you want to check which
SynchronizationContext
the code you're testing is using, you could create your own derived class that sets a flag you can check in your test. Here's an example:In
StartWorkInFirstThread
, set the context to an instance ofTestSynchronizationContext
:After you call
BeginInvoke
, you need to wait for thePost
to happen before you exit the test, so call:In
CallbackInFirstThread
you can check what context is being used with something like:The point is that there's no easy way to actually post back to the first thread, but you can check that the right context is being used so that, when your code runs in a real application, the callback will be running in the UI thread.