[英]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.WaitAny
或Task.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.