我有以下代码:
[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测试运行器将此方法称为单元测试。
答案 0 :(得分:8)
请参阅http://www.codeproject.com/KB/threads/SynchronizationContext.aspx
你需要的答案。您必须覆盖SynchronizationContext
才能正确处理您的操作。
从以下开始阅读:
请注意,DoWork已执行 线程11,与Run1相同的线程。 没有太多的SynchronizationContext 进入主线程。为什么?什么是 继续?嗯......这是其中的一部分 当你意识到什么都没有 生活自由。线程不能只是 他们之间切换上下文 必须有内置的基础设施 进入他们是为了这样做。用户界面 例如,线程使用消息 泵,并在其内 SynchronizationContext,它利用 消息泵同步到UI 线程。
答案 1 :(得分:5)
SynchronizationContext的默认实现只是在调用线程中执行传递的委托(在调用Send / Post方法的线程中,而不是捕获上下文的线程)。如果您需要某些特定行为,例如某些操作的线程关联,则应手动实现。 BCL包含一些简化UI互操作性的现成实现,如WindowsFormsSynchronizationContext或DispatcherSynchronizationContext。
答案 2 :(得分:3)
您的期望是错误的,因为没有通用的方法将委托“注入”正在运行的线程。你的“第一个线程”在测试运行器中启动,将执行一个或多个测试,然后停止 - 没有办法打断它并告诉它运行CallbackInFirstThread
。 SynchronizationContext
类在线程池中运行Post
- ed委托,因为这是它唯一的选项。
像WindowsFormsSynchronizationContext
这样的派生类利用WinForms应用程序中的消息循环将Post
ed委托传递给UI线程,但在测试运行器中没有等效的。
如果要检查您正在测试的代码所使用的SynchronizationContext
,您可以创建自己的派生类,设置可在测试中检查的标记。这是一个例子:
public class TestSynchronizationContext : SynchronizationContext
{
[ThreadStatic]
private static object _CurrentPostToken;
/// <summary>
/// Gets the context's token, if the current thread is executing a delegate that
/// was posted to this context; otherwise, null.
/// </summary>
public static object CurrentPostToken
{
get
{
return _CurrentPostToken;
}
}
public object Token { get; private set; }
/// <summary>
/// Gets a WaitHandle that is set after the context executes a posted delegate.
/// </summary>
public AutoResetEvent PostHandle { get; private set; }
public TestSynchronizationContext(object token)
{
Token = token;
PostHandle = new AutoResetEvent(false);
}
public override void Post(SendOrPostCallback d, object state)
{
try
{
_CurrentPostToken = Token;
// Execute the callback on this thread, so that we can reset the context
// when it's finished.
d(state);
}
finally
{
_CurrentPostToken = null;
}
// The test method will wait on this handle so that it doesn't exit before
// the synchronization context is called.
PostHandle.Set();
}
}
在StartWorkInFirstThread
中,将上下文设置为TestSynchronizationContext
的实例:
SynchronizationContext.SetSynchronizationContext(
new TestSynchronizationContext(new object()));
致电BeginInvoke
后,您需要在退出测试前等待Post
发生,请致电:
((TestSynchronizationContext)SynchronizationContext.Current).PostHandle.WaitOne(1000);
在CallbackInFirstThread
中,您可以查看以下内容使用的上下文:
Assert.IsNotNull(TestSynchronizationContext.CurrentPostToken);
关键是没有简单的方法来实际回发到第一个线程,但是你可以检查是否正在使用正确的上下文,以便当你的代码在真实的应用程序中运行时,回调将在UI线程。