在此代码中:
CGI or FGCI.
如果调用ButtonClick,上述哪些方法实际上是在async / await生成的底线程池中并行执行的?
我的意思是,对于使用async / await的竞争条件,我应该关注什么?所有异步方法都必须在同一个调用者的线程中执行?我应该在可能的共享状态上使用互斥吗?如果是,我怎么能检测到共享状态对象是什么?
答案 0 :(得分:6)
如果调用ButtonClick,上述哪些方法实际上是在async / await生成的底线程池中并行执行的?
仅Console.WriteLine
内的Task.Run
。
我的意思是,我对使用async / await的竞争条件有什么顾虑?
我建议您先阅读我的async
intro,其中解释await
实际上是如何运作的。
总之,async
方法通常以串行异步方式编写。以此代码为例:
CodeBeforeAwait();
await SomeOtherMethodAsync();
CodeAfterAwait();
您可以随时说CodeBeforeAwait
先执行完成,然后调用SomeOtherMethodAsync
。然后我们的方法将(异步)等待SomeOtherMethodAsync
完成,并且只有在此之后才会调用CodeAfterAwait
。
所以它是串行异步的。它以连续的方式执行,就像您期望的那样,但也有该流程中的异步点(await
)。
现在,您无法说CodeBeforeAwait
和CodeAfterAwait
将在同一个帖子中执行,至少在没有更多上下文的情况下。默认情况下,await
将恢复当前SynchronizationContext
(如果没有SyncCtx,则恢复为当前TaskScheduler
)。因此,如果上面的示例方法是在UI线程中执行的,那么您就会知道CodeBeforeAwait
和CodeAfterAwait
都将在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不必等到事件处理程序完成。