简体   繁体   English

为什么非等待任务阻塞

[英]Why is non awaited Task blocking

I am launching 2 tasks , without await -ing them , and one of them is depending on the other.我正在启动 2 个任务,没有await它们,其中一个依赖于另一个。 I am trying to understand why is the following snipped of code blocking.我试图理解为什么以下代码阻塞被截断。

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

If i create CPU-bound tasks for both my tasks it works :如果我为我的两个任务创建受CPU-bound任务,它会起作用:

var t2=Task.Run(seq.Job2);
var t1=Task.Run(seq.Job1);

I have also tried to put both tasks in a separate task from the main thread and it still blocks:我还尝试将这两个任务放在与主线程不同的任务中,但它仍然阻塞:

var bigtask=Task.Run(async()=>{
                         var t2 =seq.Job2();
                         var t1 =seq.Job1();
                     });

If i launch a task without await-ing it isn't it almost the same as starting a new CPU-bound task ?如果我在不等待的情况下启动一个任务,它与启动一个新的CPU-bound任务几乎一样吗? ( Task.Run ) ( Task.Run )

Look at your compiler warnings;看看你的编译器警告; they will tell you exactly what's going wrong.他们会确切地告诉您出了什么问题。 Specifically, you're using async without await , so those methods will run synchronously .具体来说,您使用的是没有await async ,因此这些方法将同步运行。

Task.Run executes the method on a thread pool thread, which prevents it from running synchronously. Task.Run在线程池线程上执行该方法,这会阻止它同步运行。

If i launch a task without await-ing it isn't it almost the same as [using Task.Run]?如果我在不等待的情况下启动任务,它与[使用 Task.Run] 几乎不一样吗?

Every async method starts executing synchronously ; 每个async方法开始同步执行 await is the point at which it can behave asynchronously. await是它可以异步运行的点。

async by itself doesn't use any threads (or the thread pool); async本身不使用任何线程(或线程池); it's more like a fancier syntax for callbacks.它更像是一种更高级的回调语法。 Task.Run does use the thread pool. Task.Run确实使用线程池。


To solve your underlying problem (having one task wait for another), the easiest approach is to pass the Task returned from Job1 to the Job2 method, and have Job2 await that task.要解决您的潜在问题(让一个任务等待另一个任务),最简单的方法是将从Job1返回的Task Job1Job2方法,并让Job2 await该任务。 If that's not possible, then you need an asynchronous kind of signal (not a blocking one like EventWaitHandle ).如果这是不可能的,那么您需要一种异步信号(而不是像EventWaitHandle这样的阻塞信号)。 A one-time asynchronous signal is TaskCompletionSource<T> ;一次性异步信号是TaskCompletionSource<T> SemaphoreSlim also supports asynchronous waits; SemaphoreSlim也支持异步等待; and more complex coordination primitives are part of my AsyncEx library .更复杂的协调原语是我的AsyncEx 库的一部分。

Declaring methods "async" doesn't automatically make your code multi-threaded.声明方法“异步”不会自动使您的代码成为多线程的。 You can assume that your code will run synchronously until something is "awaited".您可以假设您的代码将同步运行,直到“等待”某些东西。

The issue here is that Job2 will never return, so your code will get stuck.这里的问题是 Job2 永远不会返回,所以你的代码会卡住。 But, for example (and not an actual solution), if you did something like this:但是,例如(而不是实际的解决方案),如果你做了这样的事情:

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

Your program would actually exit, because the function would become asynchronous and return to the caller once the delay is being awaited.您的程序实际上会退出,因为一旦等待延迟,该函数将变为异步并返回给调用者。

Using synchronization primitives like "EventWaitHandle/ManualResetEvent" should usually be avoided in the TPL(async/await), because they physically block the thread, instead of releasing it and awaiting a callback.在 TPL(async/await) 中通常应该避免使用像“EventWaitHandle/ManualResetEvent”这样的同步原语,因为它们在物理上阻塞了线程,而不是释放它并等待回调。

Here is an actual solution:这是一个实际的解决方案:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM