C# 在.NET4.0中将Task与Parallel.Foreach一起使用
我开始尝试在windows窗体中添加一个进度条,用于更新在Parallel.Foreach循环中运行的代码的进度。为了做到这一点,UI线程必须可用于更新进度条。我使用了一个任务来运行Parallel.Foreach循环,以允许UI线程更新进度条 在Parallel.Foreach循环中所做的工作相当密集。在使用任务运行程序的可执行文件(而不是在visual studio中调试)后,程序变得无响应。如果我在没有任务的情况下运行程序,情况就不是这样了。我注意到这两个实例之间的关键区别是,当不运行任务时,程序占用约80%的cpu,当运行任务时,占用约5%C# 在.NET4.0中将Task与Parallel.Foreach一起使用,c#,multithreading,.net-4.0,parallel-processing,C#,Multithreading,.net 4.0,Parallel Processing,我开始尝试在windows窗体中添加一个进度条,用于更新在Parallel.Foreach循环中运行的代码的进度。为了做到这一点,UI线程必须可用于更新进度条。我使用了一个任务来运行Parallel.Foreach循环,以允许UI线程更新进度条 在Parallel.Foreach循环中所做的工作相当密集。在使用任务运行程序的可执行文件(而不是在visual studio中调试)后,程序变得无响应。如果我在没有任务的情况下运行程序,情况就不是这样了。我注意到这两个实例之间的关键区别是,当不运行任务
private void btnGenerate_Click(object sender, EventArgs e)
{
var list = GenerateList();
int value = 0;
var progressLock = new object ();
progressBar1.Maximum = list.Count();
Task t = new Task(() => Parallel.ForEach (list, item =>
{
DoWork ();
lock (progressLock)
{
value += 1;
}
}));
t.Start();
while (!t.IsCompleted)
{
progressBar1.Value = value;
Thread.Sleep (100);
}
}
旁注:我知道
Interlocked.Increment(ref int___);
代替锁工作。它被认为更有效吗
我的问题有三个方面:
1.)当负载大大减少时,为什么带有任务的程序会变得无响应
2.)使用Task运行Parallel.Foreach是否将Parallel.Foreach的线程池限制为仅运行任务的线程
3.)有没有一种方法可以使UI线程响应,而不是在不使用取消令牌的情况下休眠.1秒
我很感激任何帮助或想法,我已经花了很多时间研究这个。我也很抱歉,如果我违反了任何张贴格式或规则。我试图遵守它们,但可能遗漏了一些东西。通过使用内置的
Invoke
方法,您可以大大简化那里的代码,该方法调用所属Windows同步上下文上的委托
发件人:
在拥有控件底层窗口句柄的线程上执行指定的委托
Invoke方法向上搜索控件的父链,直到找到具有窗口句柄的控件或窗体(如果当前控件的底层窗口句柄尚不存在)
这将从任务中调用表单所属的同一UI线程上的操作
委托
现在试着回答你的问题
1.)当负载大大减少时,为什么带有任务的程序会变得无响应
我不是100%确定,但这可能与您在UI线程上锁定成员有关。如果负载更少,那么锁定将更频繁地发生,这可能导致在progressbar增加时UI线程“挂起”
您还运行了一个while循环,该循环每100毫秒睡眠一次UI线程。您将看到UI由于while循环而挂起
2.)使用Task运行Parallel.Foreach是否将Parallel.Foreach的线程池限制为仅运行任务的线程
事实并非如此。将在Parallel.ForEach调用中创建多个任务。基本的ForEach
使用a来分散工作,并且不会创建超出必要范围的任务。它批量创建任务,并处理这些批
3.)有没有一种方法可以使UI线程响应,而不是在不使用取消令牌的情况下休眠.1秒
我可以通过移除while
循环,并使用Invoke
方法直接在UI线程上执行lambda来处理这个问题。在UI线程上的循环中等待任务完成就像在UI线程上执行它一样(甚至更糟)。查看BackgroundWorker
或async
如果DoWork()
访问任何UI元素,将导致死锁发生。JohnathonSullinger-DoWork()不访问任何UI元素@Yorye Nathan-你能详细说明或举个例子吗?在今天之前,我从未做过任何关于多线程的事情。也许是与此相关的事情?对于.Net 4.5来说,这是一个很好的解决方案,但问题被标记为.Net-4.0。OP可以用一个更新进度条吗?啊,我错过了。是的,他们可以。我会用一个例子更新我的答案。谢谢你的回复!不幸的是,我正在从事的项目使用的是.NET4.0,据我所知,它不支持Progress(t)类。编辑:没有看到有人已经发现了这一点。@ENSpeer Microsoft在4.0中提供了Progres
,您只需安装NuGet软件包也很抱歉这样做,但我必须执行-1以获得建议“在UI线程的while循环中使用DoEvents()
”,这几乎从来都不好做DoEvents()
在循环中,如果您发现需要在循环中使用DoEvents()
,正确的建议是“您应该重写代码,以便不需要在UI线程上执行while循环”。您在回答中使用了正确的方式(使用Invoke
),但您不应该推荐DoEvents()
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] GenerateList() => new string[500];
void DoWork()
{
Thread.Sleep(50);
}
private void button1_Click(object sender, EventArgs e)
{
var list = GenerateList();
progressBar1.Maximum = list.Length;
Task.Run(() => Parallel.ForEach(list, item =>
{
DoWork();
// Update the progress bar on the Synchronization Context that owns this Form.
this.Invoke(new Action(() => this.progressBar1.Value++));
}));
}
}