我读过Eric lippert关于async
的{{3}},以及人们对async
关键字的混淆。他说:
it(
async
)表示“此方法包含涉及等待的控制流程 异步操作因此将被重写 编译成持续传递样式以确保 异步操作可以在正确的位置恢复此方法。“异步方法的重点是尽可能保留当前线程
我不明白这一点。如果我执行一个异步方法(Task
)并且它运行,它肯定在另一个线程上运行。
此外,如果我写一个方法使用await
,(imho)它会释放正常的控制流,而代码重构类似于“ContinueWith
”稍后,在< strong>另一个主题。
我用(控制台)测试了它:
/*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*/
结果是:
A--7
C--7
D--17 <-----D and B are on different thread
B--17
999
那么埃里克的意思是说“留在当前的线程”?
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
(获得答案后)
似乎该线程仅在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;
}
答案 0 :(得分:19)
如果我执行一个异步方法并且它运行,它肯定会在另一个线程上运行。
不,通常在另一个线程上运行。它肯定在另一个线程上运行。
暂时不要考虑线程并考虑异步的本质。异步的本质是:
假设您正在缴纳税款,并且在这个复杂的工作流程中,您需要做大量的工作。您可以执行一些操作,然后记住您的位置,然后去吃午饭。然后回来再执行一些操作,然后记住你在哪里,并喂猫。然后回来再执行一些操作,然后记住你在哪里,并洗碗。然后完成计算,并在工作流程中从中断处继续。
这是一个异步计算,但它只需要一个工作人员来完成它。拥有多个工作人员只是一种特别方便的异步方式,这不是必需的。
答案 1 :(得分:12)
Eric Lippert的“线程”术语已经简化。我的博客上有一个async
/await
intro,其中解释了await
如何捕获当前上下文并使用它来恢复async
方法。
如果您在UI上下文中,则上下文是单个UI线程,async
方法将在该线程上恢复。否则,规则会更复杂一些。特别是,控制台应用程序不提供任何上下文,因此默认情况下async
方法在线程池上恢复。
答案 2 :(得分:9)
添加了async / await支持以帮助程序员编写不冻结的GUI。在Store应用程序中特别有用,以及它被添加到C#v5的核心原因,WinRT是一个非常不友好的api,它具有许多异步方法。
“停留在同一个线程”场景在GUI应用程序中非常非常重要,因为GUI不是线程安全的。但它需要一个消息循环,这是在同一个线程上恢复异步代码的唯一方法。消息循环是producer-consumer problem的核心解决方案。显然你的程序没有一个,看起来很像一个控制台模式的应用程序。因此,您不会得到此行为,它会在工作线程上恢复。
没有太大问题,因为控制台无论如何都是线程安全的,所以实际上 它不需要在同一个线程上恢复它。好吧,主要是,当你要求输入时,不计算.NET 4.5中添加的锁。这当然也意味着你没有使用异步/等待的heckofalot,一个任务工作正常。