异步 - 留在当前线程?

Roy*_*mir 8 async-await c#-5.0 .net-4.5

我读过埃里克利珀的文章有关async,而约困惑的人曾与async关键字.他说 :

it(async)表示"此方法包含涉及等待异步操作的控制流,因此将由编译器重写为连续传递样式,以确保异步操作可以在正确的位置恢复此方法." 异步方法的全部意义你尽可能地留在当前的线程上

我不明白这一点.如果我执行一个异步方法(Task)并且它运行,它肯定会在另一个线程上运行.

此外,如果我写的方法使用await,(IMHO)它释放正常的控制流,和代码重构 相似" ContinueWith"后,在另一个线程.

我用(控制台)测试了它:

/*1*/   public void StartChain()
/*2*/   {
/*3*/           var a = FuncA();
/*4*/           Console.WriteLine(a.Result);
/*5*/   }
/*6*/   
/*7*/   public async Task < int > FuncA()
/*8*/   {
/*9*/           Console.WriteLine("A--" + Thread.CurrentThread.ManagedThreadId);
/*10*/           var t = await FuncB();
/*11*/           Console.WriteLine("B--" + Thread.CurrentThread.ManagedThreadId);
/*12*/           return t;
/*13*/   }
/*14*/   
/*15*/   public async Task < int > FuncB()
/*16*/   {
/*17*/           Console.WriteLine("C--" + Thread.CurrentThread.ManagedThreadId);
/*18*/           await Task.Delay(2000);
/*19*/           Console.WriteLine("D--" + Thread.CurrentThread.ManagedThreadId);
/*20*/           return 999;
/*21*/   }
/*22*/   
/*23*/   void Main()
/*24*/   {
/*25*/           StartChain();
/*26*/   }
/*27*/   
Run Code Online (Sandbox Code Playgroud)

结果是:

A--7
C--7
D--17         <-----D  and B are on different thread
B--17
999
Run Code Online (Sandbox Code Playgroud)

所以埃里克的意思是说"留在现在的线索"?

编辑1:

asp.net它中也返回不同的线程ID.

public async Task<int> FuncA()
{
    Response.Write("<br/>C----" + Thread.CurrentThread.ManagedThreadId);
    var t = await FuncB();
    Response.Write("<br/>D----" + Thread.CurrentThread.ManagedThreadId);
    return t;
}

public async Task<int> FuncB()
{
    Response.Write("<br/>E----" + Thread.CurrentThread.ManagedThreadId);
    await Task.Delay(2000);
    Response.Write("<br/>F----" + Thread.CurrentThread.ManagedThreadId);
    return 999;
}



protected async void Page_Load(object sender, EventArgs e)
{
    Response.Write("<br/>A----" + Thread.CurrentThread.ManagedThreadId);
    var a=await FuncA();
    Response.Write("<br/>B----" + Thread.CurrentThread.ManagedThreadId);

}

A----8
C----8
E----8
F----9
D----9
B----9
Run Code Online (Sandbox Code Playgroud)

编辑2

(得到答案后)

似乎该线程仅在GUI应用程序中提供:.我在winform上运行此代码

  public async Task<int> FuncA()
        {
            textBox1.Text +=Environment.NewLine+ "\nC----" + Thread.CurrentThread.ManagedThreadId;
            var t = await FuncB();
            textBox1.Text += Environment.NewLine + "\nD----" + Thread.CurrentThread.ManagedThreadId;
            return t;
        }

        public async Task<int> FuncB()
        {
            textBox1.Text += Environment.NewLine + "\nE----" + Thread.CurrentThread.ManagedThreadId;
            await Task.Delay(2000);
            textBox1.Text += Environment.NewLine + "\nF----" + Thread.CurrentThread.ManagedThreadId;
            return 999;
        }




        private async void Form1_Load(object sender, EventArgs e)
        {
            textBox1.Text += Environment.NewLine + "\nA----" + Thread.CurrentThread.ManagedThreadId;
            var a = await FuncA();
            textBox1.Text += Environment.NewLine + "\nB----" + Thread.CurrentThread.ManagedThreadId;
        }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Eri*_*ert 20

如果我执行一个异步方法并且它运行,它肯定会在另一个线程上运行.

不,它通常在另一个线程上运行.它并不一定在另一个线程上运行.

暂时不要考虑线程并考虑异步的本质.异步的本质是:

  • 我有一些我正在执行的工作流程.
  • 在获得信息X之前,我无法继续这个工作流程.
  • 在获得信息X之前,我会做其他事情.
  • 在未来的某个时刻,一旦我拥有X,我将回到我在工作流程中停下的地方并继续.

假设您正在缴纳税款,并且在这个复杂的工作流程中,您需要做大量的工作.您可以执行一些操作然后记住您的位置,然后去吃午餐.然后回来再进行一些操作,然后记住你的位置,喂猫.然后回来再做一些操作,然后记住你的位置,并洗碗.然后完成计算,并在工作流程中从中断处继续.

这是一个异步计算,但它只需要一个工作人员来完成它.拥有多个工作人员只是一种特别方便的异步方法,因此不是必需的.

  • 那不是异步操作.执行冗长的计算是异步操作,因为在此期间您不会阻止执行其他工作. (6认同)

Ste*_*ary 12

Eric Lippert的"线程"术语被简化了.我在我的博客上有一个async/ await介绍,解释了如何await捕获当前上下文并使用它来恢复该async方法.

如果您在UI上下文中,则上下文是单个UI线程,该async方法将在该线程上继续.否则,规则会更复杂一些.特别是,控制台应用程序不提供任何上下文,因此async默认情况下,方法会在线程池上恢复.

  • `async`并不真正在乎线程。它关心“上下文”,即“ SynchronizationContext.Current”(除非它为“ null”,在这种情况下为当前的“ TaskScheduler”)。 (2认同)
  • 请注意,在控制台应用程序中 SynchronizationContext.Current 为 null,因此您最终处于不同的线程中。在 WinForms/WPF 应用程序中,您返回到 UI 线程,在这种情况下,线程意味着线程 (2认同)

Han*_*ant 11

添加了async/await支持以帮助程序员编写不冻结的GUI.在Store应用程序中特别有用,以及它被添加到C#v5的核心原因,WinRT是一个非常不友好的api,它有许多异步方法.

"保持在同一线程"场景在GUI应用程序中非常重要,因为GUI不是线程安全的.但它需要一个消息循环,这是在同一个线程上恢复异步代码的唯一方法.消息循环是生产者 - 消费者问题的核心解决方案.显然你的程序没有一个,看起来很像一个控制台模式的应用程序.因此,您不会得到此行为,它会在工作线程上恢复.

没有太大的问题,你实际上并不需要它在同一个线程上恢复,因为无论如何控制台都是线程安全的.好吧,主要是,当你要求输入时,不计算.NET 4.5中添加的锁.这当然也意味着你没有使用异步/等待的heckofalot,一个任务工作正常.

  • 并通过删除它来解决. (3认同)