同时执行多个长 运行 进程的最简单方法 c#
Simplest way to perform multiple long running processes concurrently c#
我有一份很长的 运行ning 工作,我需要为集合中的每个项目 运行 一次。
我想同时做这些工作,尽管整个程序可以等待所有工作完成。 (稍后我可能会更改它,但现在,我让问题保持简单)
根据已经给出的一些帮助,我得到了以下模式:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var tasks = items.Select (i => DoStuffWithItem(i));
await Task.WhenAll(tasks);
}
private Task DoStuffWithItem(MyItem item)
{
//LongRunningTask
return Task.Run(async () =>
{
var returnObject = await LongRunningAsyncMethod(item);
});
}
如果我理解正确的话,这仍然是一次完成每项任务 - 毫无意义。
有人建议我将 Parallel.ForEach 与 async await 结合使用 - Parallel-ForEach 模式非常简单:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
Parallel.ForEach(items, (item) =>
{
var returnObject = LongRunningMethod(item);
}
}
现在如果我理解正确的话,我会为每个项目启动一个单独的线程来完成它的事情,但是当它这样做时,主线程将等待
但是,如果我希望程序的其余部分在 DoWork 等待 Parallel.ForEach 完成时继续执行怎么办?
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
DoINeedThisMethod(items);
}
private async void DoINeedThisMethod(ICollection<MyItem> items)
{
await Task.Run(() => {
DoStuffWithItems(items);
});
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var newobjs = new List<MyItem>();
Parallel.ForEach(items, (item) =>
{
var bc = LongRunningAsyncMethod(item);
});
}
感觉不太对劲 - 我有一个 async void 在那里,这不是好的做法。但我在这里沿着正确的路线吗?
如果我理解正确,在 DoINeedThisMethod 中,我正在启动一个新线程(通过任务)来执行 Parallel.ForEach 调用,但是在那里使用“等待”意味着主线程现在将继续,而 Parallel.ForEach完成。
其实你的第一个假设
If I understand it right though, THIS does each task one at a time
still
是错误的,很容易证明:
private static async Task Main(string[] args)
{
async Task DoStuffWithItem(int i)
{
// long running task
Console.WriteLine($"processing item {i} started");
await Task.Delay(500);
Console.WriteLine($"processing item {i} finished");
}
var items = new List<int> { 1, 2, 3 };
var tasks = items.Select(i => DoStuffWithItem(i)).ToList();
await Task.WhenAll(tasks);
Console.WriteLine("\nFinished");
}
产生以下输出:
processing item 1 started
processing item 2 started
processing item 3 started
processing item 2 finished
processing item 1 finished
processing item 3 finished
Finished
所以任务是并发执行的...
注意:如以下评论所述,'ToList()' 导致立即执行 Linq 查询(立即启动任务)。
如果没有 'ToList()',任务只会在执行 'await Task.WhenAll(tasks)'
时启动
经@Johan 证实,对DoStuffWithItem
的调用应该 同时发生。
这是因为方法returns Task
,如果省略lambda表达式可能会更清楚,这是完全没有必要的:
var tasks = items.Select(DoStuffWithItem);
此外,您不应该在 DoStuffWithItem
的实现中使用 Task.Run
。如果 LongRunningAsyncMethod
是真正的异步,它将释放线程,而 I/O 仍然发生,这反过来允许您的 Select
并行生成任务。
使用Task.Run
只会通过线程切换和分配增加不必要的开销。
你应该放弃将 Parallel.ForEach
与 await
结合的想法;它们有不同的用途。
Parallel
利用多线程,适合 CPU 绑定工作,而 async
在异步工作 (I/O) 发生时释放线程。
你应该坚持你的 Select
/ WhenAll
版本,但没有 Task.Run
,只要 LongRunningAsyncMethod
是异步实现的。
我有一份很长的 运行ning 工作,我需要为集合中的每个项目 运行 一次。
我想同时做这些工作,尽管整个程序可以等待所有工作完成。 (稍后我可能会更改它,但现在,我让问题保持简单)
根据已经给出的一些帮助,我得到了以下模式:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var tasks = items.Select (i => DoStuffWithItem(i));
await Task.WhenAll(tasks);
}
private Task DoStuffWithItem(MyItem item)
{
//LongRunningTask
return Task.Run(async () =>
{
var returnObject = await LongRunningAsyncMethod(item);
});
}
如果我理解正确的话,这仍然是一次完成每项任务 - 毫无意义。
有人建议我将 Parallel.ForEach 与 async await 结合使用 - Parallel-ForEach 模式非常简单:
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
Parallel.ForEach(items, (item) =>
{
var returnObject = LongRunningMethod(item);
}
}
现在如果我理解正确的话,我会为每个项目启动一个单独的线程来完成它的事情,但是当它这样做时,主线程将等待
但是,如果我希望程序的其余部分在 DoWork 等待 Parallel.ForEach 完成时继续执行怎么办?
public static void DoWork()
{
//Get a collection of items
var items = GetMyItems();
DoINeedThisMethod(items);
}
private async void DoINeedThisMethod(ICollection<MyItem> items)
{
await Task.Run(() => {
DoStuffWithItems(items);
});
}
private async void DoStuffWithItems(ICollection<MyItem> items)
{
var newobjs = new List<MyItem>();
Parallel.ForEach(items, (item) =>
{
var bc = LongRunningAsyncMethod(item);
});
}
感觉不太对劲 - 我有一个 async void 在那里,这不是好的做法。但我在这里沿着正确的路线吗? 如果我理解正确,在 DoINeedThisMethod 中,我正在启动一个新线程(通过任务)来执行 Parallel.ForEach 调用,但是在那里使用“等待”意味着主线程现在将继续,而 Parallel.ForEach完成。
其实你的第一个假设
If I understand it right though, THIS does each task one at a time still
是错误的,很容易证明:
private static async Task Main(string[] args)
{
async Task DoStuffWithItem(int i)
{
// long running task
Console.WriteLine($"processing item {i} started");
await Task.Delay(500);
Console.WriteLine($"processing item {i} finished");
}
var items = new List<int> { 1, 2, 3 };
var tasks = items.Select(i => DoStuffWithItem(i)).ToList();
await Task.WhenAll(tasks);
Console.WriteLine("\nFinished");
}
产生以下输出:
processing item 1 started
processing item 2 started
processing item 3 started
processing item 2 finished
processing item 1 finished
processing item 3 finished
Finished
所以任务是并发执行的...
注意:如以下评论所述,'ToList()' 导致立即执行 Linq 查询(立即启动任务)。 如果没有 'ToList()',任务只会在执行 'await Task.WhenAll(tasks)'
时启动经@Johan 证实,对DoStuffWithItem
的调用应该 同时发生。
这是因为方法returns Task
,如果省略lambda表达式可能会更清楚,这是完全没有必要的:
var tasks = items.Select(DoStuffWithItem);
此外,您不应该在 DoStuffWithItem
的实现中使用 Task.Run
。如果 LongRunningAsyncMethod
是真正的异步,它将释放线程,而 I/O 仍然发生,这反过来允许您的 Select
并行生成任务。
使用Task.Run
只会通过线程切换和分配增加不必要的开销。
你应该放弃将 Parallel.ForEach
与 await
结合的想法;它们有不同的用途。
Parallel
利用多线程,适合 CPU 绑定工作,而 async
在异步工作 (I/O) 发生时释放线程。
你应该坚持你的 Select
/ WhenAll
版本,但没有 Task.Run
,只要 LongRunningAsyncMethod
是异步实现的。