我正在努力巩固我对异步和等待的理解。我终于理解了关于上下文切换的全部内容(这都是魔术)。我写了一个简短的winform程序来玩它。请考虑以下
private async void button1_Click(object sender, EventArgs e)
{
output.Text = "start";
var intRet = await Task.Run(() => RunALot());
output.Text += " after run ";
output.Text += intRet.ToString();
}
private async Task<int> RunALot ()
{
int temp = 0;
for (int ini = 0; ini <= 40000; ini++)
{
temp += ini;
}
return temp;
}
}
我在呼叫Task.Run
时已经有RunALot.
我是否需要将RunALot
的内部包裹到另一个Task.Run
和await
中那使这真正异步?嵌套上下文有任何性能影响 - 我可以在多大程度上嵌套上下文?
答案 0 :(得分:5)
RunALot
实际上不是异步操作。您已使用async
关键字对其进行了标记,但该方法的实现只是同步完成了大量工作,然后在最后返回已完成的Task
。您的代码会生成编译器警告,告诉您这一点。
由于RunALot
是同步的,因此如果要创建一个Task
来表示在另一个线程上异步完成的同步工作。你这样做了。但是,您应该更改它的签名以使其不返回RunALot
,而不是让Task
谎言并声称是异步的,这样任何人都可以清楚地知道它它实际上并不是异步操作。
答案 1 :(得分:4)
如果某些内容是同步的,并且您只想让UI线程保持空闲,请保持方法签名不含异步任务,并使用Task.Run()包装调用。您不希望您的实现使用Task.Run()强制签署Async,因为它不是异步,它是一种同步方法,您希望UI继续响应而它处理。这个实例中的Task.Run()为您提供了。
直接连接到您的示例,您不需要RunAlot上的异步任务签名,因为它是一个CPU绑定操作,并且本质上不是异步的。
我鼓励您查看以下内容:http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
应该是什么的例子:
//tr[td/font[contains(., '202')]]/td
答案 2 :(得分:3)
请记住, async关键字不会使您的代码异步运行。正如Servy已经提到的,RunALot()不是异步方法。如果你想让它异步,你可以这样做:
public static Task<int> RunALot()
{
return Task.Run(() =>
{
int temp = 0;
for (int ini = 0; ini <= 40000; ini++)
{
temp += ini;
}
return temp;
});
}
这就是你可以在button1_Click中调用它的方法:
var intRet = await RunALot();
答案 3 :(得分:2)
您需要在RunALot中创建任务,如下所示。
private static async Task<int> RunALotAsync()
{
// For CPU bound work use Task.Run, Alternate is Task.Factory.StartNew
return await Task.Run(() =>
{
int temp = 0;
for (int ini = 0; ini <= 40000; ini++)
{
temp += ini;
}
return temp;
});
}
现在等待RunALot调用方法
var intRet = await RunALotAsync();
Read more关于正确使用async
&amp; await
。