首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等待不使用当前SynchronizationContext

等待不使用当前SynchronizationContext
EN

Stack Overflow用户
提问于 2014-10-07 02:06:33
回答 2查看 1.3K关注 0票数 4

当在异步函数中使用与外部不同的SynchronizationContext时,我会产生混乱的行为。

我的程序的大部分代码都使用一个定制的SynchronizationContext,它简单地将SendOrPostCallbacks排队并在主线程中的一个特定的已知点调用它们。我在开始的时候设置了这个自定义SynchronizationContext,当我只使用这个时,一切都很好。

我遇到的问题是,我希望在线程池中运行它们的等待延续函数。

代码语言:javascript
复制
void BeginningOfTime() {
    // MyCustomContext queues each endOrPostCallback and runs them all at a known point in the main thread.
    SynchronizationContext.SetSynchronizationContext( new MyCustomContext() ); 


    // ... later on in the code, wait on something, and it should continue inside 
    // the main thread where MyCustomContext runs everything that it has queued
    int x = await SomeOtherFunction();
    WeShouldBeInTheMainThreadNow(); // ********* this should run in the main thread
}

async int SomeOtherFunction() {
    // Set a null SynchronizationContext because this function wants its continuations 
    // to run in the thread pool.
    SynchronizationContext prevContext = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext( null );

    try {

        // I want the continuation for this to be posted to a thread pool 
        // thread, not MyCustomContext.
        await Blah();

        WeShouldBeInAThreadPoolThread(); // ********* this should run in a thread pool thread

    } finally {
        // Restore the previous SetSynchronizationContext.
        SynchronizationContext.SetSynchronizationContext( prevContext );
    }
}

我得到的行为是,每个等待之后的代码都是在一个看似随机的线程中执行的。有时,WeShouldBeInTheMainThreadNow()在线程池线程中运行,有时在主线程中运行。有时WeShouldBeInAThreadPoolThread()正在运行

我在这里没有看到任何模式,但我认为在您使用“等待”的行中设置的任何SynchronizationContext.Current都将定义等待后面的代码将在何处执行。这是一个错误的假设吗?如果是这样的话,有没有一种紧凑的方法来做我想做的事呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-10-07 12:57:04

关于await,有一种常见的误解,就是在某种程度上,调用async-implemented函数会被特别处理。

但是,await关键字对一个对象进行操作,它根本不关心可访问对象来自何处。

也就是说,您始终可以用await Blah();重写var blahTask = Blah(); await blahTask;

那么,当您以这种方式重写外部await调用时会发生什么呢?

代码语言:javascript
复制
// Synchronization Context leads to main thread;
Task<int> xTask = SomeOtherFunction();
// Synchronization Context has already been set 
// to null by SomeOtherFunction!
int x = await xTask;

然后,还有另一个问题:来自内部方法的finally是在延续中执行的,这意味着它是在线程池上执行的--因此,不仅您已经取消了SynchronizationContext,而且您的SynchronizationContext (可能)在将来的某个时候会在另一个线程上被恢复。但是,由于我并不真正理解SynchronizationContext的流动方式,所以很可能SynchronizationContext根本没有恢复,它只是设置在另一个线程上(请记住,SynchronizationContext.Current是线程本地的.)

这两个问题结合在一起,可以很容易地解释你观察到的随机性。(也就是说,您正在从多个线程操作准全局状态.)

问题的根源在于await关键字不允许对延续任务进行调度。

通常,您只需要指定“await之后的代码与await之前的代码在同一个上下文上并不重要”,在这种情况下,使用ConfigureAwait(false)是合适的;

代码语言:javascript
复制
async Task SomeOtherFunction() {
    await Blah().ConfigureAwait(false);
}

但是,如果您绝对希望指定“我希望在线程池上运行await后的代码”--这应该是罕见的,那么您不能使用await,但可以使用ContinueWith --但是,您将混合使用Task对象的多种方式,这会导致代码非常混乱。

代码语言:javascript
复制
Task SomeOtherFunction() {
    return Blah()
        .ContinueWith(blahTask => WeShouldBeInAThreadPoolThread(),
                      TaskScheduler.Default);
}
票数 1
EN

Stack Overflow用户

发布于 2014-10-07 12:04:01

我希望您的代码能够正常工作,但有几个可能的原因说明了它不能工作的原因:

  1. 确保您的SynchronizationContext在执行其连续性时是当前的。
  2. 捕获SynchronizationContext时没有严格定义它。
  3. SynchronizationContext中运行代码的正常方法是在一个方法中建立当前的代码,然后运行依赖它的另一个(可能是异步的)方法。
  4. 避免当前SynchronizationContext的正常方法是将ConfigureAwait(false)附加到所有等待的任务中。
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26227566

复制
相关文章

相似问题

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