等待不使用当前的SynchronizationContext [英] await not using current SynchronizationContext

查看:228
本文介绍了等待不使用当前的SynchronizationContext的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是比外面的一个异步函数中不同的SynchronizationContext当越来越混乱的行为。

I'm getting confusing behavior when using a different SynchronizationContext inside an async function than outside.

我的大部分程序代码都使用了简单的排队一个自定义的SynchronizationContext的SendOrPostCallbacks并在我的主线程特定的已知点调用它们。我把这个自定义的SynchronizationContext在刚开始的时候,当我只用这一个一切工作正常。

Most of my program's code uses a custom SynchronizationContext that simply queues up the SendOrPostCallbacks and calls them at a specific known point in my main thread. I set this custom SynchronizationContext at the beginning of time and everything works fine when I only use this one.

我遇到的问题是,我拥有的功能,我希望他们等待延续到线程池中运行。

The problem I'm running into is that I have functions that I want their await continuations to run in the thread pool.

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()正在运行

The behavior I'm getting is that the code right after each await is executed in a seemingly-random thread. Sometimes, WeShouldBeInTheMainThreadNow() is running in a thread pool thread and sometimes the main thread. Sometimes WeShouldBeInAThreadPoolThread() is running

我没有看到这里的模式,但我认为,无论SynchronizationContext.Current是在您使用的线路设置,等待的是一个代码以下地方的await将执行将定义。那是一个不正确的假设?如果是这样,有一个紧凑的方式做什么,我想在这里做什么?

I don't see a pattern here, but I thought that whatever SynchronizationContext.Current was set to at the line where you use await is the one that will define where the code following the await will execute. Is that an incorrect assumption? If so, is there a compact way to do what I'm trying to do here?

推荐答案

有大约一个常见的误解的await ,以某种方式调用一个异步 -implemented功能是经过特殊处理的。

There is a common misconception about await, that somehow calling an async-implemented function is treated specially.

但是,等待关键字的对象进行操作,其中awaitable对象来自它并不关心的。

However, the await keyword operates on an object, it does not care at all where the awaitable object comes from.

这是,你总是可以重写等待布拉赫(); VAR blahTask =布拉赫();等待blahTask;

That is, you can always rewrite await Blah(); with var blahTask = Blah(); await blahTask;

那么,当你重写外发生等待调用方式?

So what happens when you rewrite the outer await call that way?

// Synchronization Context leads to main thread;
Task<int> xTask = SomeOtherFunction();
// Synchronization Context has already been set 
// to null by SomeOtherFunction!
int x = await xTask;

然后,还有其他的问题:最后从内方法的延续执行,这意味着它是在线程池执行的 - 这样不仅你有没有设置你的的SynchronizationContext ,但你的的SynchronizationContext 将(可能)会在未来一段时间恢复,在另一个线程。但是,因为我真的不明白,的SynchronizationContext 中流动,这是很有可能的方式是,的SynchronizationContext 是都不能恢复,这简直是在另一个线程设置(请记住, SynchronizationContext.Current 是本地线程...)

And then, there is the other issue: The finally from the inner method is executed in the continuation, meaning that it is executed on the thread pool - so not only you have unset your SynchronizationContext, but your SynchronizationContext will (potentially) be restored at some time in the future, on another thread. However, because I do not really understand the way that the SynchronizationContext is flowed, it is quite possible that the SynchronizationContext is not restored at all, that it is simply set on another thread (remember that SynchronizationContext.Current is thread-local...)

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

These two issues, combined, would easily explain the randomness that you observe. (That is, you are manipulating quasi-global state from multiple threads...)

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

The root of the issue is that the await keyword does not allow scheduling of the continuation task.

在一般情况下,你只是想指定这不是在<$代码重要C $ C>等待来在同一背景下的代码之前等待,在这种情况下,使用 ConfigureAwait(假)将是适当的;

In general, you simply want to specify "It is not important for the code after the await to be on the same context as the code before await", and in that case, using ConfigureAwait(false) would be appropriate;

async Task SomeOtherFunction() {
    await Blah().ConfigureAwait(false);
}



不过,如果你绝对要指定我希望以后的代码等待来对线程池跑 - 这是一件好事这应该是罕见的,然后你不能用的await ,但你可以做到这一点如与 ContinueWith - 但是,你要混合使用工作对象的多种方式,并可能导致非常令人迷惑。代码

However, if you absolutely want to specify "I want the code after the await to run on the thread pool" - which is something that should be rare, then you cannot do it with await, but you can do it e.g. with ContinueWith - however, you are going to mix multiple ways of using Task objects, and that can lead to pretty confusing code.

Task SomeOtherFunction() {
    return Blah()
        .ContinueWith(blahTask => WeShouldBeInAThreadPoolThread(),
                      TaskScheduler.Default);
}

这篇关于等待不使用当前的SynchronizationContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆