簡體   English   中英

如何並行執行多個任務,並在任務完成並返回特定結果時盡快返回?

[英]How to execute a number of tasks in parallel, and return ASAP when a task completes with a specific result?

我必須執行一定數量的方法,並且以一定值結束的第一個方法必須退出循環。

代碼如下所示:

var tasks = new List<Task<bool>>();
tasks.Add(VerificheHandler.RandomString(14));
tasks.Add(VerificheHandler.RandomString(9000)); 

public bool Test()
{
    Parallel.ForEach(tasks, task =>
    {
        task.Start();
        if(task.Result)
        {
            //exit parallel and kill all
            return true;
        }
    }); 
}   
                    
public async static Task<bool> RandomString(int delay)
{
    await Task.Delay(delay);
    return true;
}

錯誤是:

task.Result
Start may not be called on a promise-style task

您可以使用ParallelLoopState.Break提前執行並行循環。 還有一個.Stop()方法。 Break 將繼續執行所有索引,直到調用 break 的索引,而 stop 不會啟動較低索引的迭代。 在這兩種情況下,循環可能已經開始或完成了更高索引的迭代。

此外,parallel.for/foreach 通常不與任務結合使用。 使方法同步會使它看起來像這樣:

    public static void Test()
    {
        Parallel.For(0, 10, (i, state) =>
        {
            var r = RandomString(100);
            if (r)
            {
                state.Break();
            }
        });
    }

    public static bool RandomString(int delay)
    {
        Thread.Sleep(delay);
        return true;
    }

您提供的示例無法編譯,因為您不能將Task<bool>存儲在Task<string>的集合中。

如果我正確理解您的問題,那么您正在尋找解決方案:

  • 同時啟動多個方法
  • 只要其中一種方法返回給定值,就停止執行。

為了實現這樣的解決方案,我們必須使用CancellationToken來停止任何正在進行的操作。 所以修改后的RandomString可能如下所示:

public static async Task<string> RandomString(int delay, CancellationToken token)
{
    try
    {
        await Task.Delay(delay, token);
    }
    catch (OperationCanceledException) 
    {
        Console.WriteLine("Task has been cancelled " + delay);
        return null;
    }
    Console.WriteLine("Finished " + delay);
    return "Random " + delay;
}
  • 我在該方法中添加了一個新的CancellationToken參數,然后將其傳遞給Task.Delay
    • 如果沒有取消,那么它將打印出一些調試信息並返回一些隨機字符串。
    • 如果在Task.Delay期間有取消,那么我們淺化OperationCanceledException並打印出一些調試信息。

為了能夠冷啟動這個方法,我們可以在它周圍引入一個簡單的包裝器:

public static Func<CancellationToken, Task<string>> RandomStringWrapper(int delay)
    => (ct) => RandomString(delay, ct);
  • 它不會立即調用RandomString ,而是返回一個 function ,它需要一個CancellationToken
  • 每當傳遞CancellationToken時,任務就會運行。

所以,現在我們可以引入一個足夠通用的擴展方法來支持多種不同的異步方法:

public static class TaskEx
{
    public static async Task RunUntil<T>(this IEnumerable<Func<CancellationToken, Task<T>>> asyncFunctions, T exitCondition, CancellationTokenSource cts)
    {
        var jobs = asyncFunctions.Select(fn => fn(cts.Token)).ToList();
        while (true)
        {
            var fastest = await Task.WhenAny(jobs);
            if (fastest.Result.Equals(exitCondition))
            {
                cts.Cancel(true);
                return;
            }

            jobs.Remove(fastest);

            if (jobs.Count == 0)
                return;
        }
    }
}
  • asyncFunctions :它是函數的集合。 每個人都希望CancellationToken返回一個Task<T>
  • exitCondition :顧名思義...
  • ctsCancellationToken提供程序。
  • 首先,我們通過將CancellationToken傳遞給異步方法來啟動它們。
  • 在循環中,我們等待fastest完成。
  • 如果fastest的結果等於exitCondition則我們取消剩余的任務。
  • 如果它們不相等,則我們從作業中刪除已完成的jobs並重新運行相同的循環,直到有要執行的作業。

現在,讓我們把所有這些放在一起:

static async Task Main(string[] args)
{
    var cts = new CancellationTokenSource();
    var tasks = new[] { RandomStringWrapper(14), RandomStringWrapper(600), RandomStringWrapper(500) };
    await tasks.RunUntil("Random 500", cts);
}

output 將是:

Finished 14
Finished 500
Task has been cancelled 600

暫無
暫無

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

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