我目前正试图在一行的末尾连续打印点作为一种不确定的进展形式,而大量的任务列表正在运行,使用以下代码:
tasks
List<Task> tasks = new List<Task>();
来自tasks.Add(Task.Run(() => someMethodAsync()));
,
并{{1}}发生了10000次。
这段代码目前有效,但这是实现这一目标的正确方法,这是最具成本效益的方法吗?
答案 0 :(得分:11)
正如托马斯所说,当然有几种方法可以解决这个问题。我立即想到的那个是:
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);
请注意,此方法确实使用await
,因此要求此方法为async
。通常对于控制台应用程序,我建议Main
只调用MainAsync
,因此您的阻塞(或主循环)都在一行代码中,而不与任何逻辑混合。
答案 1 :(得分:9)
肯定有几种方法可以解决,其中一种是你的。但是,启动长时间运行的任务并不是一个好习惯,特别是当它们不做同步等待时(即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);
}
}
答案 2 :(得分:3)
托马斯&#39;答案很好,你应该接受它。我将提供一个代码增量较小的版本:
替换它:
Task progress = new Task(() => { while (!entireTask.IsCompleted) { Console.Write("."); System.Threading.Thread.Sleep(1000); } });
progress.Start();
有了这个:
Task progressTask = Task.Run(async () => {
while (!entireTask.IsCompleted) {
Console.Write(".");
await Task.Delay(1000);
}
});
这更有效,而且我认为更清晰的代码。
托马斯&#39;一旦&#34; wholeTask&#34;已经完成了。