Sic*_*Sic 7 .net c# task-parallel-library async-await
让我们说我有几个任务:
void Sample(IEnumerable<int> someInts)
{
var taskList = someInts.Select(x => DownloadSomeString(x));
}
async Task<string> DownloadSomeString(int x) {...}
Run Code Online (Sandbox Code Playgroud)
我想得到第一个成功任务的结果.所以,基本的解决方案是写下这样的东西:
var taskList = someInts.Select(x => DownloadSomeString(x));
string content = string.Empty;
Task<string> firstOne = null;
while (string.IsNullOrWhiteSpace(content)){
try
{
firstOne = await Task.WhenAny(taskList);
if (firstOne.Status != TaskStatus.RanToCompletion)
{
taskList = taskList.Where(x => x != firstOne);
continue;
}
content = await firstOne;
}
catch(...){taskList = taskList.Where(x => x != firstOne);}
}
Run Code Online (Sandbox Code Playgroud)
但是这个解决方案似乎运行N+( - N1)+ .. + K任务.哪里N是someInts.Count和K在任务第一次成功的任务的位置,这样它重新运行所有任务,除了一个由WhenAny抓获.那么,有没有办法让第一个任务成功完成并运行最多的N任务?(如果成功的任务将是最后一个)
Ser*_*rvy 10
您需要做的就是创建一个TaskCompletionSource,为每个任务添加一个延续,并在第一个任务成功完成时设置它:
public static Task<T> FirstSuccessfulTask<T>(IEnumerable<Task<T>> tasks)
{
var taskList = tasks.ToList();
var tcs = new TaskCompletionSource<T>();
int remainingTasks = taskList.Count;
foreach (var task in taskList)
{
task.ContinueWith(t =>
{
if (task.Status == TaskStatus.RanToCompletion)
tcs.TrySetResult(t.Result);
else
if (Interlocked.Decrement(ref remainingTasks) == 0)
tcs.SetException(new AggregateException(tasks.SelectMany(t1 => t1.Exception.InnerExceptions)));
});
}
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
没有结果的任务版本:
public static Task FirstSuccessfulTask(IEnumerable<Task> tasks)
{
var taskList = tasks.ToList();
var tcs = new TaskCompletionSource<bool>();
int remainingTasks = taskList.Count;
foreach (var task in taskList)
{
task.ContinueWith(t =>
{
if (task.Status == TaskStatus.RanToCompletion)
tcs.TrySetResult(true);
else
if (Interlocked.Decrement(ref remainingTasks) == 0)
tcs.SetException(new AggregateException(
tasks.SelectMany(t1 => t1.Exception.InnerExceptions)));
});
}
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
"第一个成功的任务"的问题是如果所有任务都失败了怎么办?拥有永不完成的任务是一个非常糟糕的主意.
我假设你想要传播最后一个任务的异常,如果它们都失败了.考虑到这一点,我会说这样的事情是合适的:
async Task<Task<T>> FirstSuccessfulTask(IEnumerable<Task<T>> tasks)
{
Task<T>[] ordered = tasks.OrderByCompletion();
for (int i = 0; i != ordered.Length; ++i)
{
var task = ordered[i];
try
{
await task.ConfigureAwait(false);
return task;
}
catch
{
if (i == ordered.Length - 1)
return task;
continue;
}
}
return null; // Never reached
}
Run Code Online (Sandbox Code Playgroud)
该解决方案建立在OrderByCompletion扩展方法是一部分的我AsyncEx库 ; Jon Skeet和Stephen Toub也存在替代实施.
直接的解决方案是等待任何任务,检查它是否处于 RanToCompletion 状态,如果不是,则再次等待除已经完成的任务之外的任何任务。
async Task<TResult> WaitForFirstCompleted<TResult>( IEnumerable<Task<TResult>> tasks )
{
var taskList = new List<Task<TResult>>( tasks );
while ( taskList.Count > 0 )
{
Task<TResult> firstCompleted = await Task.WhenAny( taskList ).ConfigureAwait(false);
if ( firstCompleted.Status == TaskStatus.RanToCompletion )
{
return firstCompleted.Result;
}
taskList.Remove( firstCompleted );
}
throw new InvalidOperationException( "No task completed successful" );
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2447 次 |
| 最近记录: |