簡體   English   中英

為什么非等待任務阻塞

[英]Why is non awaited Task blocking

我正在啟動 2 個任務,沒有await它們,其中一個依賴於另一個。 我試圖理解為什么以下代碼阻塞被截斷。

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 )

看看你的編譯器警告; 他們會確切地告訴您出了什么問題。 具體來說,您使用的是沒有await async ,因此這些方法將同步運行。

Task.Run在線程池線程上執行該方法,這會阻止它同步運行。

如果我在不等待的情況下啟動任務,它與[使用 Task.Run] 幾乎不一樣嗎?

每個async方法開始同步執行 await是它可以異步運行的點。

async本身不使用任何線程(或線程池); 它更像是一種更高級的回調語法。 Task.Run確實使用線程池。


要解決您的潛在問題(讓一個任務等待另一個任務),最簡單的方法是將從Job1返回的Task Job1Job2方法,並讓Job2 await該任務。 如果這是不可能的,那么您需要一種異步信號(而不是像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");
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM