我在数组中有一系列任务。如果任务为“良好”,则返回字符串。如果为“不良”:则返回null。
我希望能够并行运行所有任务,一旦第一个任务返回“ Good”,然后取消其他任务并获得“ Good”结果。
我现在正在这样做,但是问题是所有任务都需要运行,然后我遍历它们以寻找第一个好的结果。
List<Task<string>> tasks = new List<Task<string>>();
Task.WaitAll(tasks.ToArray());
答案 0 :(得分:1)
答案 1 :(得分:1)
我希望能够并行运行所有任务,一旦第一个任务返回“ Good”,然后取消其他任务并获得“ Good”结果。
这是一种误解,因为Cancellation in TPL is co-operative,所以一旦启动任务,就无法取消它。 CancellationToken
可以在启动Task之前工作,也可以在以后引发异常(如果要求取消),这意味着启动并采取必要的操作,例如从逻辑中抛出自定义异常。
检查以下query,其中列出了许多有趣的答案,但没有一个取消。以下也是一种可能的选择:
public static class TaskExtension<T>
{
public static async Task<T> FirstSuccess(IEnumerable<Task<T>> tasks, T goodResult)
{
// Create a List<Task<T>>
var taskList = new List<Task<T>>(tasks);
// Placeholder for the First Completed Task
Task<T> firstCompleted = default(Task<T>);
// Looping till the Tasks are available in the List
while (taskList.Count > 0)
{
// Fetch first completed Task
var currentCompleted = await Task.WhenAny(taskList);
// Compare Condition
if (currentCompleted.Status == TaskStatus.RanToCompletion
&& currentCompleted.Result.Equals(goodResult))
{
// Assign Task and Clear List
firstCompleted = currentCompleted;
break;
}
else
// Remove the Current Task
taskList.Remove(currentCompleted);
}
return (firstCompleted != default(Task<T>)) ? firstCompleted.Result : default(T);
}
}
var t1 = new Task<string>(()=>"bad");
var t2 = new Task<string>(()=>"bad");
var t3 = new Task<string>(()=>"good");
var t4 = new Task<string>(()=>"good");
var taskArray = new []{t1,t2,t3,t4};
foreach(var tt in taskArray)
tt.Start();
var finalTask = TaskExtension<string>.FirstSuccess(taskArray,"good");
Console.WriteLine(finalTask.Result);
您甚至可以返回Task<Task<T>>
而不是Task<T>
来进行必要的逻辑处理
答案 2 :(得分:1)
我正在研究Task.WhenAny()
,它将在第一个“完成”任务上触发。不幸的是,从这个意义上说,已完成的任务基本上是任何东西……甚至一个例外也被视为“已完成”。据我所知,没有其他方法可以检查您所谓的“好”值。
虽然我不认为您的问题有令人满意的答案,但我认为可能有替代方法可以解决您的问题。考虑使用Parallel.ForEach
。
Parallel.ForEach(tasks, (task, state) =>
{
if (task.Result != null)
state.Stop();
});
state.Stop()
在发现非空结果时将停止执行并行循环。
除了具有发现“好”值时停止执行的能力外,它在很多(但不是全部)情况下的性能也会更好。
答案 3 :(得分:0)
使用Task.WhenAny
返回完成的任务。检查它是否为空。如果是这样,请将其从列表中删除,然后再次调用Task.WhenAny。
如果情况不错,请取消列表中的所有任务(它们都应带有CancellationTokenSource.Token
。
编辑:
所有任务都应使用相同的CancellationTokenSource.Token
。然后,您只需取消一次。
这里有一些代码需要澄清:
private async void button1_Click(object sender, EventArgs e)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
List<Task<string>> tasks = new List<Task<string>>();
tasks.Add(Task.Run<string>(() => // run your tasks
{
while (true)
{
if (cancellationTokenSource.Token.IsCancellationRequested)
{
return null;
}
return "Result"; //string or null
}
}));
while (tasks.Count > 0)
{
Task<string> resultTask = await Task.WhenAny(tasks);
string result = await resultTask;
if (result == null)
{
tasks.Remove(resultTask);
}
else
{
// success
cancellationTokenSource.Cancel(); // will cancel all tasks
}
}
}