簡體   English   中英

如何僅在第一個線程完成之前阻塞?

[英]How can I block only until the first thread finishes?

最近,我遇到了這樣一種情況:能夠派生一堆線程,阻塞並等待一個確切的答案(第一個到達),取消其余線程然后取消阻塞。

例如,假設我有一個采用種子值的搜索功能。 讓我們規定搜索功能可以平凡地並行化。 此外,我們的搜索空間包含許多潛在的解,並且對於某些種子值,該函數將無限期搜索,但是至少一個種子值將在合理的時間內產生一個解。

如果我能夠天真地並行進行此搜索,那將是很棒的,例如:

let seeds = [|0..100|]
Array.Parallel.map(fun seed -> Search(seed)) seeds

可悲的是, Array.Parallel.map將阻塞,直到所有線程完成。 笨蛋 我總是可以在搜索功能中設置超時,但是我幾乎可以肯定要等待運行時間最長的線程。 此外,對於某些問題,超時時間可能不夠長。

簡而言之,我只想對任意函數使用UNIX套接字select()調用之類的東西。 這可能嗎? 如上所述,它不必是相當漂亮的數據並行抽象,也不必是F#代碼。 我什至很樂意使用本機庫並通過P / Invoke調用它。

您可以創建一堆任務,然后分別使用Task.WaitAnyTask.WhenAny同步等待第一個任務完成,或者創建一個將在第一個任務完成時完成的任務。

一個簡單的同步示例:

var tasks = new List<Task<int>>();
var cts = new CancellationTokenSource();

for (int i = 0; i < 10; i++)
{
    int temp = i;
    tasks.Add(Task.Run(() =>
    {
        //placeholder for real work of variable time
        Thread.Sleep(1000 * temp);

        return i;
    }, cts.Token));
}

var value = Task.WaitAny(tasks.ToArray());
cts.Cancel();

或對於異步版本:

public static async Task<int> Foo()
{
    var tasks = new List<Task<int>>();
    var cts = new CancellationTokenSource();

    for (int i = 0; i < 10; i++)
    {
        int temp = i;
        tasks.Add(Task.Run(async () =>
        {
            await Task.Delay(1000 * temp, cts.Token);
            return temp;
        }));
    }

    var value = await await Task.WhenAny(tasks);
    cts.Cancel();

    return value;
}
let rnd = System.Random()

let search seed = async {
    let t = rnd.Next(10000)
    //printfn "seed: %d  ms: %d" seed t
    do! Async.Sleep t
    return sprintf "seed %d finish" seed
}

let processResult result = async {
    //Todo:
    printfn "%s" result
}

let cts = new System.Threading.CancellationTokenSource()
let ignoreFun _ = () //if you don't want handle

let tasks = 
    [0..10]
    |> List.map (fun i ->
        async {
            let! result = search i
            do! processResult result
            cts.Cancel()
        }
    )

Async.StartWithContinuations(Async.Parallel tasks, ignoreFun, ignoreFun, ignoreFun, cts.Token)

嘗試使用事件對象同步所有線程,當找到設置事件的解決方案時,所有其他線程必須定期檢查事件狀態,並停止執行(如果已設置)。

有關更多詳細信息,請參見此處

這似乎對我有用

namespace CancellParallelLoops
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = Enumerable.Range(0, 10000000).ToArray();
            CancellationTokenSource cts = new CancellationTokenSource();

            // Use ParallelOptions instance to store the CancellationToken
            ParallelOptions po = new ParallelOptions();
            po.CancellationToken = cts.Token;
            po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
            Console.WriteLine("Press any key to start. Press 'c' to cancel.");
            Console.ReadKey();

            // Run a task so that we can cancel from another thread.
            Task.Factory.StartNew(() =>
            {
                if (Console.ReadKey().KeyChar == 'c')
                    cts.Cancel();
                Console.WriteLine("press any key to exit");
            });

            try
            {
                Parallel.ForEach(nums, po, (num) =>
                {
                    double d = Math.Sqrt(num);
                    Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);
                    if (num == 1000) cts.Cancel();
                    po.CancellationToken.ThrowIfCancellationRequested();
                });
            }
            catch (OperationCanceledException e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadKey();
        }
    }
}

暫無
暫無

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

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