C# 实际执行的代码是什么;“多线程”;在异步/等待模式中?

C# 实际执行的代码是什么;“多线程”;在异步/等待模式中?,c#,multithreading,asynchronous,async-await,C#,Multithreading,Asynchronous,Async Await,在此代码中: public async Task v_task() { await Task.Run(() => Console.WriteLine("Hello!")); } public async void v1() { await v_task(); // some other actions... } public void ButtonClick() { v1(); Console.WriteLine("Hi!"); } 如果调用

在此代码中:

public async Task v_task()
{
    await Task.Run(() => Console.WriteLine("Hello!"));
}

public async void v1()
{
    await v_task();
    // some other actions...
}

public void ButtonClick()
{
    v1();

    Console.WriteLine("Hi!");
}
如果调用ButtonClick,则在异步/等待生成的较低线程池中实际并行执行上述哪些方法


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

如果调用异步函数,线程将执行此函数,直到到达等待状态

如果不使用async await,线程将产生处理,直到等待的代码完成,并在等待后继续执行语句

但是,当您使用async await时,您告诉编译器,每当线程必须等待某件事情时,它可以做一些其他事情来代替等待,线程将执行这些其他事情,直到您说:现在等待,直到原始事情完成

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

例如:

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中的wait已完成,线程也不会执行下一条语句,直到您等待SaveTask。这样做的效果是,如果SaveAsync中的等待完成,DoSomethingElse将不会被中断

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

如果调用ButtonClick,则在异步/等待生成的较低线程池中实际并行执行上述哪些方法

只有
任务.Run中的
控制台.WriteLine

我的意思是,对于使用async/await时的竞争条件,我应该关注什么

我建议您从阅读我的开始,它解释了
await
实际上是如何工作的

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

CodeBeforeAwait();
await SomeOtherMethodAsync();
CodeAfterAwait();
您总是可以说
codebeforewait
将首先执行到完成,然后调用
SomeOtherMethodAsync
。然后我们的方法将(异步)等待
SomeOtherMethodAsync
完成,并且只有在完成之后才会调用
codeaterwait

所以它是串行异步的。它以串行方式执行,就像您期望的那样,但在该流中也有一个异步点(
wait

现在,您不能说
codebeaforeawait
codebeaterwait
将在同一个线程中执行,至少没有更多上下文<默认情况下,code>Wait
将在当前的
SynchronizationContext
中恢复(如果没有SyncCtx,则在当前的
TaskScheduler
中恢复)。因此,如果上面的示例方法是在UI线程中执行的,那么您将知道
codebeforewait
codefterwait
都将在UI线程中执行。但是,如果它是在没有上下文的情况下执行的(即,来自后台线程或控制台主线程),则
codeaterwait
可能会在不同的线程上运行

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

还要注意,您的原始示例使用了
Task.Run
,它明确地将工作放在线程池上。这与
async
/
await
大不相同,您必须将其视为多线程

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

对。例如,如果您的代码使用了
Task.Run
,则需要将其视为一个单独的线程。(请注意,使用
wait
,完全不与其他线程共享状态会容易得多-如果您可以保持后台任务的纯净,那么使用它们会容易得多)

如果是,如何检测哪些是共享状态对象


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

默认情况下,所有异步延续都在它们启动的同一同步上下文中执行。在控制台应用程序中没有同步上下文,所有任务都将在线程池中运行。所以是的,你应该了解比赛情况。共享状态对象是在所有任务中访问的对象。Hello@VMAtm!因此,调用图将在相同的同步上下文中执行(我认为这与线程相同),直到它找到第一个'wait'关键字?此时(执行调用图中的第一个“await”),在“await”下面的代码将在另一个线程中执行?嘿,Stephen,谢谢你非常好的回答!总之:调用图中的所有等待调用都将在同一线程中执行;如果我通过task.Run或TaskCompletionSource显式创建任务,则代码将仅在另一个后台线程中执行?换句话说,如果我没有实现任何将通过Task.Run执行的方法,我的所有代码将在同一个线程中执行?伙计,如果最后一个问题的答案是肯定的:这真是太美了!非常感谢,我现在就看你的文章。@ptr0x:不太好;它将在相同的上下文中执行。如果您只使用UI上下文,那么是的,这与“同一线程”具有相同的含义。但是如果你在控制台应用程序中运行相同的代码,那么不,它可以是不同的线程。如果您的代码显式覆盖默认的上下文捕获行为(即,
ConfigureAwait(false)
),那么它可能会使用上下文或