我需要并行启动任务,但我选择使用Task.Run
而不是Parallel.Foreach
,因此我可以在所有任务完成后获得一些反馈并启用UI控件。
private async void buttonStart_Click(object sender, EventArgs e)
{
var cells = objectListView.CheckedObjects;
if(cells != null)
{
List<Task> tasks = new List<Task>();
foreach (Cell c in cells)
{
Cell cell = c;
var progressHandler = new Progress<string>(value =>
{
cell.Status = value;
});
var progress = progressHandler as IProgress<string>;
Task t = Task.Run(() =>
{
progress.Report("Starting...");
int a = 123;
for (int i = 0; i < 200000; i++)
{
a = a + i;
Task.Delay(500).Wait();
}
progress.Report("Done");
});
tasks.Add(t);
}
await Task.WhenAll(tasks);
Console.WriteLine("Done, enabld UI controls");
}
}
所以我期待的是,我在UI“开始...”中几乎可以立即看到所有项目。我实际看到的是前4个项目是“正在开始......”(我猜因为每个线程都使用了所有4个CPU内核),然后每秒或更少的新项目是“正在启动”。我总共有37个项目,所有项目启动所有任务大约需要30秒。
如何让它尽可能平行?
答案 0 :(得分:2)
如何让它尽可能平行?
内部for循环的一部分是模拟长时间运行的CPU绑定作业,我想尽可能同时开始。
它已尽可能平行。启动所有具有CPU限制工作的37个线程不会让它更快,因为你显然是在4核机器上运行它。有4个核心,因此一次只能运行4个线程。其他33个线程将等待4个正在运行。他们只会出现同时运行。
也就是说,如果你真的希望启动所有这些线程池线程,你可以通过调用ThreadPool.SetMinThreads
来实现这一点。
我需要并行启动任务,但我选择使用Task.Run而不是Parallel.Foreach,所以我可以在所有任务完成后获得一些反馈并启用UI控件。
由于您要完成并行工作,因此应使用Parallel
。如果你想要await
的漂亮的UI-thread-thread行为,那么你可以使用一个await Task.Run
,如下所示:
private async void buttonStart_Click(object sender, EventArgs e)
{
var cells = objectListView.CheckedObjects;
if (cells == null)
return;
var workItems = cells.Select(c => new
{
Cell = c,
Progress = new Progress<string>(value => { c.Status = value; }),
}).ToList();
await Task.Run(() => Parallel.ForEach(workItems, item =>
{
var progress = item.Progress as IProgress<string>();
progress.Report("Starting...");
int a = 123;
for (int i = 0; i < 200000; i++)
{
a = a + i;
Thread.Sleep(500);
}
progress.Report("Done");
}));
Console.WriteLine("Done, enabld UI controls");
}
答案 1 :(得分:0)
我说,它尽可能平行。如果你有4个核心,你可以并行运行4个线程。
如果你可以在等待&#34;延迟&#34;时做一些事情,那么看一下异步编程(其中一个线程可以运行多个任务&#34;一次&#34;,因为大多数是等待什么)。
编辑:您还可以在自己的任务中运行Parallel.ForEach
,并await
:
private async void buttonStart_Click(object sender, EventArgs e)
{
var cells = objectListView.CheckedObjects;
if(cells != null)
{
await Task.Run( () => Parallel.ForEach( cells, c => ... ) );
}
}
答案 2 :(得分:0)
我认为它依赖于你的taskcreation-options。
TaskCreationOptions.LongRunning
在这里您可以找到更多信息:
https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcreationoptions(v=vs.110).aspx
但是你必须知道,该任务使用的线程池具有有限的最大线程数。您可以使用 LongRunning 发出信号,表示此任务需要很长时间,并且不应阻塞您的池。我认为创建一个长时间运行的任务会更复杂,因为调度程序可能会创建一个新线程。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TaskTest
{
internal class Program
{
private static void Main(string[] args)
{
var demo = new Program();
demo.SimulateClick();
Console.ReadLine();
}
public void SimulateClick()
{
buttonStart_Click(null, null);
}
private async void buttonStart_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
for (var i = 0; i < 36; i++)
{
var taskId = i;
var t = Task.Factory.StartNew((() =>
{
Console.WriteLine($"Starting Task ({taskId})");
for (var ii = 0; ii < 200000; ii++)
{
Task.Delay(TimeSpan.FromMilliseconds(500)).Wait();
var s1 = new string(' ', taskId);
var s2 = new string(' ', 36-taskId);
Console.WriteLine($"Updating Task {s1}X{s2} ({taskId})");
}
Console.Write($"Done ({taskId})");
}),TaskCreationOptions.LongRunning);
tasks.Add(t);
}
await Task.WhenAll(tasks);
Console.WriteLine("Done, enabld UI controls");
}
}
}