代码如下:
static async Task Main(string[] args)
{
var t = new Task(async () => await AsyncTest());
t.Start();
t.Wait();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
我期望t.Wait()
实际上会等待AsyncTest
方法完成,并且输出将是:
Method finished
Main finished
实际上,输出只有Main finished
。当您在AsyncTest中按下await Task.Delay(2000)
时,Wait()即完成。如果将t.Wait()
替换为await t
/ await Task.WhenAll(t)
/ Task.WaitAll(t)
,也会发生同样的情况。
解决方案是将方法重写为同步实现,或者直接在AsyncTest()上调用Wait()。但是,问题是为什么它以这种奇怪的方式工作?
P.S。这是代码的简化版本。我试图实现延迟的任务执行。实际上,Task对象是由程序的一个部分创建的,然后在某些特定条件之后由另一部分执行。
UPD:将var t = new Task(async () => await AsyncTest())
重写为var t = new Task(()=> AsyncTest().Wait())
也解决了该问题。尽管我还是不太明白为什么Task无法在委托中使用async / await正常工作。
答案 0 :(得分:6)
我仍然不太明白为什么Task在委托内部使用async / await无法正常工作。
因为Task
构造函数仅用于创建Delegate Tasks-即表示要运行的同步代码的任务。由于代码是同步的,因此您的async
lambda被视为async void
lambda,这意味着Task
实例将不会异步地等待AsyncTest
完成。>
更重要的是,the Task
constructor should never, ever be used in any code, anywhere, for any reason。它实际上有零个有效用例。
Task.Task
可以很好地替代Task.Run
,可以理解async
lambda。
在我的真实程序中,任务已推迟执行。在一个位置创建任务对象,然后在程序的另一部分执行特定条件之后再创建。
在这种情况下,请使用asynchronous delegate。具体来说,Func<Task>
。
static async Task Main(string[] args)
{
Func<Task> func = AsyncTest;
// Later, when we're ready to run.
await func();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
答案 1 :(得分:0)
@JonSkeet的quote:
您的异步方法只返回void,这意味着没有简单的方法 等待它完成的任何事情。 (您几乎应该总是 避免使用异步void方法。它们实际上仅适用于 为了订阅事件。)
因此,请看这段代码:
var t = new Task(async () => await AsyncTest());
看看Task
构造函数的签名:
public Task(Action action);
public Task(Action action, CancellationToken cancellationToken);
public Task(Action action, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state);
public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken);
public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
它们都是Actions
,并且您知道Action
的返回类型为void
。
static async Task Main(string[] args)
{
// best way to do it
await AsyncTest();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
// Don't use thread sleep, await task delay is fine
// Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
答案 2 :(得分:-2)
选中此
public static async Task Main(string[] args)
{
var t = Task.Factory.StartNew(async () => await AsyncTest());
//t.Start();
t.Result.Wait();
t.Wait();
Console.WriteLine("Main finished");
}
private static async Task AsyncTest()
{
//Thread.Sleep(2000);
await Task.Delay(2000);
Console.WriteLine("Method finished");
}
和这个link
答案 3 :(得分:-4)
请勿使用var task = new Task(...); task.Start();
,而应仅使用Task.Run
。
在第一种情况下,使用构造函数Task(Action action)
,因此实际上您实际上并不在等待异步方法。如果您在不使用asyn-await语法糖的情况下检查real
C#,则会看到AsyncVoidMethodBuilder
。如果使用Task.Run
,则使用Task.Run(Func<Task> func)
,以便收到“实际”任务。
因此,如果要使用构造函数,则应使用注释中的方法:new Task<Task>
。