为什么非等待任务阻塞
Why is non awaited Task blocking
我正在启动 2 个任务,没有 await
-ing 它们,其中一个依赖于另一个。
我试图理解为什么以下代码被屏蔽了。
public class Tasks {
EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset);
public async Task Job1() {
Console.WriteLine("Finished job1");
handle.Set();
}
public async Task Job2() {
handle.WaitOne();
Console.WriteLine("Doing Job2 work");
}
}
class Program {
static async Task Main(string[] args) {
Tasks seq = new Tasks();
var t2 =seq.Job2();
var t1 =seq.Job1();
await Task.WhenAll(t1, t2);
Console.WriteLine("finished both");
}
}
如果我为我的两个任务创建 CPU-bound
个任务,它会起作用:
var t2=Task.Run(seq.Job2);
var t1=Task.Run(seq.Job1);
我也尝试过将这两个任务放在与主线程不同的任务中,但它仍然阻塞:
var bigtask=Task.Run(async()=>{
var t2 =seq.Job2();
var t1 =seq.Job1();
});
如果我在没有等待的情况下启动任务,这不是几乎与启动新的 CPU-bound
任务一样吗? (Task.Run
)
查看你的编译器警告;他们会准确地告诉您出了什么问题。具体来说,您使用的是 async
而没有 await
,因此这些方法将 运行 同步 .
Task.Run
在线程池线程上执行该方法,这会阻止它 运行 同步。
If i launch a task without await-ing it isn't it almost the same as [using Task.Run]?
Every async
method starts executing synchronously; await
是它可以异步运行的点。
async
本身不使用任何线程(或线程池);它更像是一种更高级的回调语法。 Task.Run
确实使用了线程池。
要解决您的潜在问题(让一个任务等待另一个任务),最简单的方法是将 Job1
返回的 Task
传递给 Job2
方法,并让 Job2
await
那个任务。如果那不可能,那么您需要一种异步信号(而不是像 EventWaitHandle
这样的阻塞信号)。一次性异步信号是TaskCompletionSource<T>
; SemaphoreSlim
也支持异步等待;更复杂的协调原语是我的 AsyncEx library.
的一部分
声明方法 "async" 不会自动使您的代码成为多线程。您可以假设您的代码将 运行 同步,直到 "awaited".
这里的问题是 Job2 永远不会 return,因此您的代码会卡住。
但是,例如(而不是实际的解决方案),如果你做了这样的事情:
public async Task Job2()
{
await Task.Delay(1000);
handle.WaitOne();
Console.WriteLine("Doing Job2 work");
}
您的程序实际上会退出,因为一旦等待延迟,该函数将变为异步并 return 给调用者。
在 TPL(async/await) 中通常应避免使用像 "EventWaitHandle/ManualResetEvent" 这样的同步原语,因为它们实际上会阻塞线程,而不是释放线程并等待回调。
这是一个实际的解决方案:
public class Tasks
{
SemaphoreSlim semaphore = new SemaphoreSlim(0);
public async Task Job1()
{
Console.WriteLine("Finished job1");
semaphore.Release();
}
public async Task Job2()
{
await semaphore.WaitAsync();
Console.WriteLine("Doing Job2 work");
}
}
class Program
{
static async Task Main(string[] args)
{
Tasks seq = new Tasks();
var t2 = seq.Job2();
var t1 = seq.Job1();
await Task.WhenAll(t1, t2);
Console.WriteLine("finished both");
}
}
我正在启动 2 个任务,没有 await
-ing 它们,其中一个依赖于另一个。
我试图理解为什么以下代码被屏蔽了。
public class Tasks {
EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset);
public async Task Job1() {
Console.WriteLine("Finished job1");
handle.Set();
}
public async Task Job2() {
handle.WaitOne();
Console.WriteLine("Doing Job2 work");
}
}
class Program {
static async Task Main(string[] args) {
Tasks seq = new Tasks();
var t2 =seq.Job2();
var t1 =seq.Job1();
await Task.WhenAll(t1, t2);
Console.WriteLine("finished both");
}
}
如果我为我的两个任务创建 CPU-bound
个任务,它会起作用:
var t2=Task.Run(seq.Job2);
var t1=Task.Run(seq.Job1);
我也尝试过将这两个任务放在与主线程不同的任务中,但它仍然阻塞:
var bigtask=Task.Run(async()=>{
var t2 =seq.Job2();
var t1 =seq.Job1();
});
如果我在没有等待的情况下启动任务,这不是几乎与启动新的 CPU-bound
任务一样吗? (Task.Run
)
查看你的编译器警告;他们会准确地告诉您出了什么问题。具体来说,您使用的是 async
而没有 await
,因此这些方法将 运行 同步 .
Task.Run
在线程池线程上执行该方法,这会阻止它 运行 同步。
If i launch a task without await-ing it isn't it almost the same as [using Task.Run]?
Every async
method starts executing synchronously; await
是它可以异步运行的点。
async
本身不使用任何线程(或线程池);它更像是一种更高级的回调语法。 Task.Run
确实使用了线程池。
要解决您的潜在问题(让一个任务等待另一个任务),最简单的方法是将 Job1
返回的 Task
传递给 Job2
方法,并让 Job2
await
那个任务。如果那不可能,那么您需要一种异步信号(而不是像 EventWaitHandle
这样的阻塞信号)。一次性异步信号是TaskCompletionSource<T>
; SemaphoreSlim
也支持异步等待;更复杂的协调原语是我的 AsyncEx library.
声明方法 "async" 不会自动使您的代码成为多线程。您可以假设您的代码将 运行 同步,直到 "awaited".
这里的问题是 Job2 永远不会 return,因此您的代码会卡住。 但是,例如(而不是实际的解决方案),如果你做了这样的事情:
public async Task Job2()
{
await Task.Delay(1000);
handle.WaitOne();
Console.WriteLine("Doing Job2 work");
}
您的程序实际上会退出,因为一旦等待延迟,该函数将变为异步并 return 给调用者。
在 TPL(async/await) 中通常应避免使用像 "EventWaitHandle/ManualResetEvent" 这样的同步原语,因为它们实际上会阻塞线程,而不是释放线程并等待回调。
这是一个实际的解决方案:
public class Tasks
{
SemaphoreSlim semaphore = new SemaphoreSlim(0);
public async Task Job1()
{
Console.WriteLine("Finished job1");
semaphore.Release();
}
public async Task Job2()
{
await semaphore.WaitAsync();
Console.WriteLine("Doing Job2 work");
}
}
class Program
{
static async Task Main(string[] args)
{
Tasks seq = new Tasks();
var t2 = seq.Job2();
var t1 = seq.Job1();
await Task.WhenAll(t1, t2);
Console.WriteLine("finished both");
}
}