实际执行了什么代码"多线程"在async / await模式?

时间:2016-04-18 10:09:21

标签: c# multithreading asynchronous async-await

在此代码中:

CGI or FGCI.

如果调用ButtonClick,上述哪些方法实际上是在async / await生成的底线程池中并行执行的?

我的意思是,对于使用async / await的竞争条件,我应该关注什么?所有异步方法都必须在同一个调用者的线程中执行?我应该在可能的共享状态上使用互斥吗?如果是,我怎么能检测到共享状态对象是什么?

2 个答案:

答案 0 :(得分:6)

  

如果调用ButtonClick,上述哪些方法实际上是在async / await生成的底线程池中并行执行的?

Console.WriteLine内的Task.Run

  

我的意思是,我对使用async / await的竞争条件有什么顾虑?

我建议您先阅读我的async intro,其中解释await实际上是如何运作的。

总之,async方法通常以串行异步方式编写。以此代码为例:

CodeBeforeAwait();
await SomeOtherMethodAsync();
CodeAfterAwait();

您可以随时说CodeBeforeAwait先执行完成,然后调用SomeOtherMethodAsync。然后我们的方法将(异步)等待SomeOtherMethodAsync完成,并且只有在此之后才会调用CodeAfterAwait

所以它是串行异步的。它以连续的方式执行,就像您期望的那样,但也有该流程中的异步点(await)。

现在,您无法CodeBeforeAwaitCodeAfterAwait将在同一个帖子中执行,至少在没有更多上下文的情况下。默认情况下,await将恢复当前SynchronizationContext(如果没有SyncCtx,则恢复为当前TaskScheduler)。因此,如果上面的示例方法是在UI线程中执行的,那么您就会知道CodeBeforeAwaitCodeAfterAwait都将在UI线程上执行。但是,如果它是在没有上下文的情况下执行的(即,从后台线程或控制台主线程执行),则CodeAfterAwait可以在不同的线程上运行。

请注意,即使某个方法的某些部分在不同的线程上运行,运行时也会在继续使用该方法之前设置任何障碍,因此无需阻止变量访问。

另请注意,您的原始示例使用Task.Run,它明确地将工作放在线程池上。这与async / await完全不同,您肯定必须将其视为多线程。

  

我应该在可能的共享状态下使用互斥吗?

是。例如,如果您的代码使用Task.Run,那么您需要将其视为单独的线程。 (请注意,对于await很多更容易与其他线程无法共享状态 - 如果您可以保持后台任务的纯粹性,那么它们会更容易与...合作。

  

如果是,我怎么能检测到共享状态对象是什么?

与任何其他类型的多线程代码相同的答案:代码检查。

答案 1 :(得分:2)

如果你调用异步函数,你的线程将执行此函数,直到它到达等待。

如果您没有使用async-await,那么该线程将在等待代码完成之前产生处理,并在等待之后继续使用该语句。

但是当你使用async-await时,你告诉编译器,每当线程必须等待某事时,你还有其他一些事情可以做而不是等待,线程会做其他事情,直到你说:now等待你原来的事情结束。

由于对异步函数的调用,我们确信其中的某个地方应该有等待。请注意,如果您调用的async函数没有等待,则会收到编译器警告该函数将同步运行。

示例:

private async void OnButton1_clickec(object sender, ...)
{
    string dataToSave = ...;
    var saveTask = this.MyOpenFile.SaveAsync(dataToSave);

    // the thread will go into the SaveAsync function and will
    // do all things until it sees an await.
    // because of the async SaveAsync we know there is somewhere an await
    // As you didn't say await this.MyOpenfile.SaveAsync
    // the thread will not wait but continue with the following
    // statements:

    DoSomethingElse()
    await saveTask;

    // here we see the await. The thread was doing something else,
    // finished it, and now we say: await. That means it waits until its
    // internal await is finished and continues with the statements after
    // this internal await.

请注意,即使等待SaveAsync中的某个地方完成,在您等待SaveTask之前,线程也不会执行下一个语句。这会导致如果SaveAsync中的await结束,DoSomethingElse将不会被中断。

因此,通常创建一个不返回任务或任务的异步函数是没有用的。 TResult> 唯一的例外是事件处理程序。 GUI不必等到事件处理程序完成。