Async C#Tasks.WhenAny
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Async C#Tasks.WhenAny相关的知识,希望对你有一定的参考价值。
我需要运行任务和计时器,如果计时器更早结束,则运行另一个任务并继续等待第一个和第二个任务的答案并返回第一个完成的结果。我需要重新启动计时器。我想做那样的事情
foreach (var uri in Addresses)
{
var webRequest = CreateRequest(uri + "?query=" + query);
resultTasks.Add(ProcessRequestAsync(webRequest));
}
Task<string> firstFinishedTask = await Task.WhenAny(resultTasks);
<-- if timer end early -->
resultTasks.Add(<--newTask-->);
Task<string> firstFinishedTask = await Task.WhenAny(resultTasks);
但此时我发现了一个错误,因为该列表已经有一个正在运行的任务。
你能推荐什么?
换句话说:我有任务列表
List<Task> tasksList = new List<Task>();
<-- some initialization of this list-->
然后我启动Task.WhenAny(tasksList);
如何将其他任务添加到WhenAny?
答案
我想我找到了一个可能的解决方案。但我使用Timer
而不是Task.Delay(...)
。
下面是WinForms应用程序示例的代码,其主要形式包含Button btnStart
,ProgressBar progressBar
和RichTextBox log
。
如果需要,CreateNextTask()
将创建一个新的Task
。
GetWaitTask()
扮演计时器的角色。
看起来很有效。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TaskAndTimer
{
public partial class MainForm : Form
{
private int[] taskConfigs = { 1200, 1300, 1100, 800 };
private Queue<int> queue;
public MainForm()
{
InitializeComponent();
}
private async void btnStart_Click(object sender, EventArgs e)
{
queue = new Queue<int>(taskConfigs);
Task<int> firstTask;
Task<int>[] tasks = new Task<int>[] { };
int result = -1;
progressBar.Style = ProgressBarStyle.Marquee;
do
{
log.AppendText($"Next round...{Environment.NewLine}");
log.AppendText($"-------------{Environment.NewLine}");
tasks =
tasks
.Concat(new[] { GetWaitTask(), CreateNextTask() })
.ToArray();
LogTasks("Current tasks:", tasks);
firstTask = await Task.WhenAny(tasks);
LogTasks("First task:", firstTask);
tasks = tasks.Except(new[] { firstTask }).ToArray();
LogTasks("Remaining tasks:", tasks);
result = await firstTask;
log.AppendText($"-------------{Environment.NewLine}");
log.AppendText(Environment.NewLine);
}
while (result == -1 && queue.Any());
log.AppendText($"result = [{result}]
");
progressBar.Style = ProgressBarStyle.Blocks;
}
private Task<int> GetWaitTask()
{
return Task.Run(async () => { await Task.Delay(1000); return -1; });
}
private Task<int> CreateNextTask()
{
if (queue.Any())
{
int data = queue.Dequeue();
return Task.Run(async () => { await Task.Delay(data); return data; });
}
return Task.FromResult(-1);
}
private void LogTasks(string message, params Task<int>[] tasks)
{
log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff"));
log.AppendText($"> {message}");
foreach (var task in tasks)
{
log.AppendText($" [{task.Id}];");
}
log.AppendText(Environment.NewLine);
}
}
}
结果如下:
Next round...
-------------
2018-02-23 19:30:31.2273143> Current tasks: [2]; [4];
2018-02-23 19:30:32.2300773> First task: [2];
2018-02-23 19:30:32.2330774> Remaining tasks: [4];
-------------
Next round...
-------------
2018-02-23 19:30:32.2375774> Current tasks: [4]; [14]; [16];
2018-02-23 19:30:32.4392640> First task: [4];
2018-02-23 19:30:32.4452575> Remaining tasks: [14]; [16];
-------------
result = [1200]
另一答案
如果你必须继续等待所有任务,那么试试await Task.WhenAll(Task);
。
此外,以这种方式订购事物似乎很麻烦,可能使用递归函数来发送您的每个任务,并让您的主线程在一个带有诊断计时器的while循环中。然后最后在循环结束后等待所有。
readonly List<Task<T>> _taskList = new List<Task<T>>();
void fireThreads()
{
var timer = Stopwatch.StartNew();
var taskLoop = Task.Factory.StartNew(()=> DO STUFF);
_taskList.Add(taskLoop);
while(await Task.WhenAll(TaskList))
{
if (timer.ElapsedMilliseconds / 1000 > TargetTime)
fireThreads();
}
}
绝对不是最好的代码示例,但这样的东西可能会起作用。
以上是关于Async C#Tasks.WhenAny的主要内容,如果未能解决你的问题,请参考以下文章
Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题