簡體   English   中英

如何讓mutliple線程等待一個單獨的任務?

[英]How to have mutliple threads await a single Task?

我讀過這個: 從多個線程等待相同的任務是否可以 - 等待線程安全嗎? 我不清楚答案,所以這是一個特定的用例。

我有一個執行一些異步網絡I / O的方法。 多個線程可以同時觸發此方法,我不希望它們全部調用網絡請求,如果請求已在進行中我想阻止/等待第二個+線程,並且一旦單個IO操作就讓它們全部恢復已經完成了。

應該怎么寫下面的偽代碼? 我猜每個調用線程確實需要獲得自己的Task ,所以每個人都可以得到它自己的延續,所以不是返回currentTask我應該回到一個新的Task是由“內”完成TaskDoAsyncNetworkIO 有沒有干凈的方法來做到這一點,還是我必須手動滾動它?

static object mutex = new object();
static Task currentTask;

async Task Fetch()
{
    lock(mutex)
    {
        if(currentTask != null)
            return currentTask;
    }

    currentTask = DoAsyncNetworkIO();
    await currentTask;

    lock(mutex)
    {
        var task = currentTask;
        currentTask = null;
        return task;
    }
}

您可以使用SemaphoreSlim來確保只有一個線程實際執行后台線程。

假設您的基本任務(實際執行IO的那個)在一個名為baseTask()的方法中,我將仿效:

static async Task baseTask()
{
    Console.WriteLine("Starting long method.");
    await Task.Delay(1000);
    Console.WriteLine("Finished long method.");
}

然后你可以像這樣初始化一個SemaphoreSlim ,有點像AutoResetEvent ,初始狀態設置為true

static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);

然后在一個方法中調用baseTask() ,該方法檢查signal ,看看這是否是第一個嘗試運行baseTask()線程,如下所示:

static async Task<bool> taskWrapper()
{
    bool firstIn = await signal.WaitAsync(0);

    if (firstIn)
    {
        await baseTask();
        signal.Release();
    }
    else
    {
        await signal.WaitAsync();
        signal.Release();
    }

    return firstIn;
}

然后你的多個線程將等待taskWrapper()而不是直接等待baseTask()

將它完全放在可編輯的控制台應用程序中:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            for (int it = 0; it < 10; ++it)
            {
                Console.WriteLine($"\nStarting iteration {it}");
                Task[] tasks = new Task[5];

                for (int i = 0; i < 5; ++i)
                    tasks[i] = Task.Run(demoTask);

                Task.WaitAll(tasks);
            }

            Console.WriteLine("\nFinished");                  
            Console.ReadLine();
        }

        static async Task demoTask()
        {
            int id = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine($"Thread {id} starting");

            bool firstIn = await taskWrapper();

            Console.WriteLine($"Task {id}: executed: {firstIn}");
        }

        static async Task<bool> taskWrapper()
        {
            bool firstIn = await signal.WaitAsync(0);

            if (firstIn)
            {
                await baseTask();
                signal.Release();
            }
            else
            {
                await signal.WaitAsync();
                signal.Release();
            }

            return firstIn;
        }

        static async Task baseTask()
        {
            Console.WriteLine("Starting long method.");
            await Task.Delay(1000);
            Console.WriteLine("Finished long method.");
        }

        static readonly SemaphoreSlim signal = new SemaphoreSlim(1, 1);
    }
}

(這些方法都是靜態的,因為它們位於控制台應用程序中;在實際代碼中,它們是非靜態方法。)

await根本不一定使用continuation( Task.ContinueWith kind)。 即使它確實如此,您也可以在一個Task上擁有多個延續 - 它們不能全部同步運行(如果您有同步上下文,則可能會遇到一些問題)。

請注意,您的偽代碼不是線程安全的 - 您不能只執行currentTask = DoAsyncNetworkIO(); 在鎖外。 只有await本身是線程安全的,即便如此,只是因為你正在等待的Task類以線程安全的方式實現await契約。 任何人都可以寫自己的等待/等待,所以一定要注意:)

暫無
暫無

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

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