为什么非等待任务被阻止



我正在启动 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)

查看编译器警告;它们会准确地告诉您出了什么问题。具体来说,您使用的是没有awaitasync,因此这些方法将同步运行。

Task.Run在线程池线程上执行该方法,这会阻止它同步运行。

如果我在没有等待的情况下启动任务,它与[使用Task.Run]几乎相同吗?

每个async方法开始同步执行;await是它可以异步行为的点。

async本身不使用任何线程(或线程池);它更像是一种更高级的回调语法。Task.Run确实使用线程池。


要解决您的根本问题(让一个任务等待另一个任务),最简单的方法是将从Job1返回的Task传递给Job2方法,并Job2await该任务。如果这是不可能的,那么你需要一个异步的信号(而不是像EventWaitHandle这样的阻塞信号)。一次性异步信号TaskCompletionSource<T>;SemaphoreSlim还支持异步等待;更复杂的协调原语是我的 AsyncEx 库的一部分。

声明方法"异步"不会自动使代码成为多线程。您可以假设您的代码将同步运行,直到"等待"某些内容。

这里的问题是 Job2 永远不会返回,所以你的代码会卡住。 但是,例如(而不是实际的解决方案),如果您执行以下操作:

public async Task Job2()
{
await Task.Delay(1000);
handle.WaitOne();
Console.WriteLine("Doing Job2 work");
}

您的程序实际上会退出,因为该函数将变得异步,并在等待延迟后返回给调用方。

在 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");
}
}

相关内容

  • 没有找到相关文章

最新更新