任何人都可以解释为什么这个代码只是在WhenAll触发后才会达到死胡同?
主要代码:
class AsyncTests
{
public async void Start()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting whole process, calling await DoWork1()");
await Task.WhenAll(DoWork1(), DoWork2());
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished awaiting DoWork1 and DoWork2");
}
public async Task DoWork1()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork1");
await DoNothing();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork1");
}
public async Task DoWork2()
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork2");
await DoNothing();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork2");
}
public Task DoNothing() { return new Task(() => { /* do nothing */ }); }
}
程序控制代码:
static void Main(string[] args)
{
var x = new AsyncTests();
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - Main ... calling Start()");
Task.Run(() => x.Start());
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} - Main ... start is running");
Console.ReadKey();
}
输出:
Thread:1 - Main ... calling Start()
Thread:1 - Main ... start is running
Thread:4 - starting whole process, calling await DoWork1()
Thread:4 - starting DoWork1
Thread:4 - starting DoWork2
UPDATE
为了使它更清晰,让我们改变它,以便DoNothing实际上调用Thread.Sleep(2000),我的目标是同时运行两个线程休眠,并希望使用async / await模式来实现这一点。
如果我将“DoNothing”更改为执行睡眠的异步任务,那么我被告知我需要await
运算符。这意味着我需要编写另一个async
方法等待。那么在运营商方面结束这一系列呼叫的最佳方式是什么?
有人可以展示如何实现上述目标的最佳实践示例吗?
你永远不会在DoNothing中开始你的任务。
分离任务创建和执行
Task类还提供了初始化任务但不安排执行任务的构造函数。出于性能原因,Task.Run或TaskFactory.StartNew方法是创建和调度计算任务的首选机制,但对于必须分离创建和调度的方案,您可以使用构造函数然后调用Task.Start方法来安排以后执行的任务。
不要创建要返回的任务,而是让语言为您完成。
public async Task DoNothing() { }
上面实际上什么也没做,将返回一个处于完成状态的Task
,可以等待。
您当前正在执行此操作的方式,即创建了Task,但它从未启动或设置为Completed,因此等待它将永远锁定程序。
为了补充已经给出的答案和评论,我想展示一个代码示例,以我在测试中的方式工作。它显示了执行流程+ for info,特定时间执行的托管线程ID。
主要代码:
class AsyncTests
{
public async Task StartAsync()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting whole process, calling await DoWork1Async()");
await Task.WhenAll(DoWork1Async(), DoWork2Async());
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished awaiting DoWork1Async and DoWork2Async");
}
public async Task DoWork1Async()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork1Async");
await Sleep();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork1Async");
}
public async Task DoWork2Async()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - starting DoWork2Async");
await Sleep();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - Thread:{Thread.CurrentThread.ManagedThreadId} - finished DoWork2Async");
}
public async Task Sleep()
{
await Task.Delay(2000);
}
}
调用代码(注意,为了使这个异步运行,我不得不省略await
操作符,它在consider applying the 'await' operator
方法调用上引发警告StartAsync()
。
static void Main(string[] args)
{
var x = new AsyncTests();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - {Thread.CurrentThread.ManagedThreadId} - Main ... calling Start()");
x.StartAsync();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff} - {Thread.CurrentThread.ManagedThreadId} - Main ... start is running");
Console.ReadKey();
}
最后,输出 - 正如预期的那样,显示代码执行/控制返回到真正异步操作所需的位置。正如预期的那样,两个不同的池线程用于运行睡眠。
10:43:36.515 - 1 - Main ... calling Start()
10:43:36.546 - Thread:1 - starting whole process, calling await DoWork1Async()
10:43:36.547 - Thread:1 - starting DoWork1Async
10:43:36.561 - Thread:1 - starting DoWork2Async
10:43:36.562 - 1 - Main ... start is running
10:43:38.581 - Thread:4 - finished DoWork2Async
10:43:38.582 - Thread:5 - finished DoWork1Async
10:43:38.582 - Thread:5 - finished awaiting DoWork1Async and DoWork2Async