有人可以向我解释为什么在Working!
之后显示第二个Done
?
Stating
Doing Work
Working!
Done
Working!
Work completed
第二个问题为什么我不能像下面那样得到任务结果:
Task result = await LongOperation();
最后一个问题是在我的代码中代表await / async使用Task.Run
的原因是什么?它可以在哪里使用,还是不好用?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting");
var worker = new Worker();
worker.DoWork();
Console.WriteLine("Done");
Console.ReadKey();
}
}
public class Worker
{
public async void DoWork()
{
Console.WriteLine("Doing work");
await LongOperation();
await LongOperation();
Console.WriteLine("Work completed");
}
private async Task LongOperation()
{
Console.WriteLine("Working!");
await Task.Delay(200);
}
}
答案 0 :(得分:0)
这是因为您已声明DoWork()
异步,返回类型为void
。这使得它成为了一场“火上浇油”而又“忘掉了”。异步运行的方法。如果您DoWork()
返回Task
而不是void
,则可以await
它,这样可以确保您完成"完成" DoWork()
完成执行后会发生消息。
此外,await
会解包任务结果,因此您无法等待它并同时获取该值。如果您希望直接使用Task
结果,请不要await
。
您指定的代码中没有特定区域,您应该使用Task.Run()
。
答案 1 :(得分:0)
有理由"完成!"出现的时间比您预期的要早,因为您在await
前面不 worker.DoWork();
(DoWork
需要返回Task
才能使用await
)。所以会发生什么,DoWork()
会立即返回执行延迟到另一个线程,并立即转到下一行,即控制台写入"完成"。
关于Task result = await LongOperation();
,await
接受参数等待对象(即Task
),代表您检查其.Result
属性,提取结果并将其返回。因此,您可以删除await
以获取任务实例,也可以将await
置于等待任务完成并提取调用的实际返回值。
使用Task.Run
或通过任务工厂有几个原因,一个例子是传递lambda函数来执行(可能带有闭包)。我会参考TPL上的MSDN库进行详细的潜水。
答案 2 :(得分:0)
Working
会在Done!
之后显示,因为在您的static void Main
中,您还没有等待worker.DoWork();
完成,因此程序会执行下一行。您应该像这样更改DoWork
方法:
public async Task DoWork()
{
//
}
并将调用更改为:
worker.DoWork().GetAwaiter().GetResult();
您不能,因为使用await
您的LongOperation
将不会返回Task
。例如,如果您有这样的签名,当您使用await
时,您需要打开结果:
public Task<int> GiveANumberAsync()
{
return Task.FromResult(12);
}
int result = await GiveANumberAsync();
对于这个问题,我认为我不能比斯蒂芬in this answer更好地解释,他说:
使用Task.Run调用CPU绑定方法。
答案 3 :(得分:0)
逐一提问:
Main
中,您不等待DoWork
。这意味着它被调用,然后Main
中的代码继续执行,而不等待DoWork
完成。因此,&#34;完成&#34;在第二个&#34; Working&#34;。await
关键字的作用。来自documentation:await运算符应用于异步方法中的任务,以暂停方法的执行,直到等待的任务完成。该任务代表了正在进行的工[...] 应用await运算符的任务通常是调用实现基于任务的异步模式的方法的返回值。示例包括
Task
或Task<TResult>
类型的值。
实际上,为了await
方法,它必须具有返回类型Task<T>
,其中T是代码中return
的实际类型。您的方法LongOperation
实际上并没有在此示例中返回任何内容。
Task.Run
用于将任何方法或代码块作为任务运行。它可以被解雇和忘记(Task.Run([something];
),或等待任何其他任务(await Task.Run([something]);
。请参阅它的文档here,虽然我不确定它会是多少用户Stephen Cleary在this回答中解释了这一点以及其他一些相关信息。