clo*_*ypt 11 .net c# asynchronous task async-await
我正在尝试在一行的末尾连续打印点作为一种不确定的进展形式,同时运行一大堆任务,使用以下代码:
start = DateTime.Now;
Console.Write("*Processing variables");
Task entireTask = Task.WhenAll(tasks);
Task progress = new Task(() => { while (!entireTask.IsCompleted) { Console.Write("."); System.Threading.Thread.Sleep(1000); } });
progress.Start();
entireTask.Wait();
timeDiff = DateTime.Now - start;
Console.WriteLine("\n*Operation completed in {0} seconds.", timeDiff.TotalSeconds);
Run Code Online (Sandbox Code Playgroud)
哪里tasks是List<Task> tasks = new List<Task>();,
和tasks.Add(Task.Run(() => someMethodAsync()));已经发生10000周的时期.
这个代码目前有效,但这是实现这一目标的正确方法,这是最具成本效益的方法吗?
Ste*_*ary 12
正如托马斯所说,肯定有几种方法可以解决这个问题.我立即想到的那个是:
start = DateTime.Now;
Console.Write("*Processing variables");
Task entireTask = Task.WhenAll(tasks);
while (await Task.WhenAny(entireTask, Task.Delay(1000)) != entireTask)
{
Console.Write(".");
}
timeDiff = DateTime.Now - start;
Console.WriteLine("\n*Operation completed in {0} seconds.", timeDiff.TotalSeconds);
Run Code Online (Sandbox Code Playgroud)
请注意,此方法确实使用await,因此需要此方法async.通常对于控制台应用程序,我建议只进行一次Main调用MainAsync,因此阻塞(或主循环)都在一行代码中,而不是与任何逻辑混合.
肯定有几种方法可以解决,其中一种是你的.但是,启动长时间运行的任务并不是一个好习惯,特别是当它们不做同步等待时(即Thread.Sleep).
您应该考虑在技术和域部分中重构代码.技术部分是:
以下代码可能有助于更好地理解这一点.它启动四个任务,模拟不同的异步操作,并等待所有这些操作完成.如果这花费的时间超过250毫秒,则WhenAllEx的调用将继续调用lambda以重新执行进度报告.
static void Main(string[] args)
{
var tasks = Enumerable.Range(0, 4).Select(taskNumber => Task.Run(async () =>
{
Console.WriteLine("Task {0} starting", taskNumber);
await Task.Delay((taskNumber + 1) * 1000);
Console.WriteLine("Task {0} stopping", taskNumber);
})).ToList();
// Wait for all tasks to complete and do progress report
var whenAll = WhenAllEx(
tasks,
_ => Console.WriteLine("Still in progress. ({0}/{1} completed)", _.Count(task => task.IsCompleted), tasks.Count()));
// Usually never wait for asynchronous operations unless your in Main
whenAll.Wait();
Console.WriteLine("All tasks finished");
Console.ReadKey();
}
/// <summary>
/// Takes a collection of tasks and completes the returned task when all tasks have completed. If completion
/// takes a while a progress lambda is called where all tasks can be observed for their status.
/// </summary>
/// <param name="tasks"></param>
/// <param name="reportProgressAction"></param>
/// <returns></returns>
public static async Task WhenAllEx(ICollection<Task> tasks, Action<ICollection<Task>> reportProgressAction)
{
// get Task which completes when all 'tasks' have completed
var whenAllTask = Task.WhenAll(tasks);
for (; ; )
{
// get Task which completes after 250ms
var timer = Task.Delay(250); // you might want to make this configurable
// Wait until either all tasks have completed OR 250ms passed
await Task.WhenAny(whenAllTask, timer);
// if all tasks have completed, complete the returned task
if (whenAllTask.IsCompleted)
{
return;
}
// Otherwise call progress report lambda and do another round
reportProgressAction(tasks);
}
}
Run Code Online (Sandbox Code Playgroud)