首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么SynchronizationContext不能正常工作?

为什么SynchronizationContext不能正常工作?
EN

Stack Overflow用户
提问于 2010-10-08 16:24:08
回答 3查看 5.9K关注 0票数 3

我有以下代码:

代码语言:javascript
复制
[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()方法。也就是这样的东西:

代码语言:javascript
复制
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的意思吗?但实际上,我有以下输出:

代码语言:javascript
复制
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将此方法称为单元测试。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-10-08 17:05:49

请参阅http://www.codeproject.com/KB/threads/SynchronizationContext.aspx

这是你需要的答案。您必须重写SynchronizationContext以使其正确处理您的操作。

从以下开始阅读:

请注意,DoWork是在线程11上执行的,该线程与Run1相同。没有太多的SynchronizationContext进入主线程。为什么?怎么回事?好吧..。这是当你意识到生活中没有任何东西是免费的时候。线程不能只是在它们之间切换上下文,它们必须有一个内置的基础设施才能做到这一点。例如,UI线程使用消息泵,在它的SynchronizationContext中,它利用消息泵来同步到UI线程。

票数 8
EN

Stack Overflow用户

发布于 2010-10-08 17:01:32

SynchronizationContext的默认实现只是在调用线程中执行传递的委托(在调用发送/Post方法的线程中,而不是捕获上下文的线程中)。如果您需要一些特定的行为,比如某些操作的线程关联,您应该手动实现这一点。BCL包含很少的开箱即用的实现来简化UI互操作性,比如WindowsFormsSynchronizationContextDispatcherSynchronizationContext.

票数 5
EN

Stack Overflow用户

发布于 2013-01-03 17:32:43

您的期望是错误的,因为没有将委托“注入”到正在运行的线程的一般方法。您的“第一个线程”在测试运行程序中启动,将执行一个或多个测试,然后停止--无法中断它并让它运行CallbackInFirstThreadSynchronizationContext类在线程池中运行Post-ed委托,因为这是它唯一的选项。

WindowsFormsSynchronizationContext这样的派生类利用WinForms应用程序中的消息循环将Post-ed委托传递给UI线程,但是在测试运行程序中没有等效的。

如果要检查正在测试的代码使用的是哪个SynchronizationContext,可以创建自己的派生类,该类设置可以在测试中签入的标志。下面是一个例子:

代码语言:javascript
复制
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实例

代码语言:javascript
复制
SynchronizationContext.SetSynchronizationContext(
        new TestSynchronizationContext(new object()));

调用BeginInvoke之后,需要等待Post在退出测试之前发生,因此调用:

代码语言:javascript
复制
((TestSynchronizationContext)SynchronizationContext.Current).PostHandle.WaitOne(1000);

CallbackInFirstThread中,您可以检查以下内容所使用的上下文:

代码语言:javascript
复制
Assert.IsNotNull(TestSynchronizationContext.CurrentPostToken);

要点是,实际上回发到第一个线程并不是一种简单的方法,但您可以检查是否使用了正确的上下文,以便当您的代码在实际应用程序中运行时,回调将在UI线程中运行。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3892453

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档