我正在尝试实现一个 Parallel.ForEach 循环来替换旧的 foreach
循环,但是我在更新我的 UI 时遇到了问题(我有一个计数器显示类似“x/y 文件已处理”的内容).我制作了一个并行 Forloop
示例来说明我的问题(标签未更新)。
using System;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;
namespace FormThreadTest
{
public partial class Form1 : Form
{
private SynchronizationContext m_sync;
private System.Timers.Timer m_timer;
private int m_count;
public Form1()
{
InitializeComponent();
m_sync = SynchronizationContext.Current;
m_count = 0;
m_timer = new System.Timers.Timer();
m_timer.Interval = 1000;
m_timer.AutoReset = true;
m_timer.Elapsed += new System.Timers.ElapsedEventHandler(m_timer_Elapsed);
m_timer.Start();
}
private void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Task.Factory.StartNew(() =>
{
m_sync.Post((o) =>
{
label1.Text = m_count.ToString();
Application.DoEvents();
}, null);
});
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Parallel.For(0, 25000000, delegate(int i)
{
m_count = i;
});
});
}
}
}
如果我更改按钮单击事件方法,并添加一个 Thread.Sleep(),它似乎为 UI 更新线程提供了时间来完成它的工作:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
Parallel.For(0, 25000000, delegate(int i)
{
m_count = i;
Thread.Sleep(10);
});
});
}
有没有办法避免 sleep ,还是我需要在那里 sleep ?除非我这样做,否则我的用户界面似乎不会更新标签?我觉得很奇怪,因为我可以移动应用程序窗口(它不会锁定)——那么为什么标签不更新,我怎样才能改变我的代码以更好地支持 Parallel.For(Each) 和 UI 更新?
我一直在寻找解决方案,但我似乎找不到任何东西(或者我可能在寻找错误的东西?)。
问候 西蒙
最佳答案
我有类似的要求来更新我的 GUI,因为结果来自 Parallel.ForEach()。不过,我走的路和你走的路很不一样。
public partial class ClassThatUsesParallelProcessing
{
public event ProcessingStatusEvent StatusEvent;
public ClassThatUsesParallelProcessing()
{ }
private void doSomethingInParallel()
{
try
{
int counter = 0;
int total = listOfItems.Count;
Parallel.ForEach(listOfItems, (instanceFromList, state) =>
{
// do your work here ...
// determine your progress and fire event back to anyone who cares ...
int count = Interlocked.Increment( ref counter );
int percentageComplete = (int)((float)count / (float)total * 100);
OnStatusEvent(new StatusEventArgs(State.UPDATE_PROGRESS, percentageComplete));
}
}
catch (Exception ex)
{
}
}
}
然后您的 GUI 将具有类似于以下内容的内容:
private void ProcessingStatusEventHandler(object sender, StatusEventArgs e)
{
try
{
if (e.State.Value == State.UPDATE_PROGRESS)
{
this.BeginInvoke((ProcessHelperDelegate)delegate
{
this.progressBar.Value = e.PercentageComplete;
}
}
}
catch { }
}
我在这里想说明的唯一一点是,您可以确定何时确定您在循环中的进度是有意义的。而且,由于这些循环迭代是在后台线程上进行的,因此您需要将 GUI 控件更新逻辑编码回主(调度)线程。这只是一个简单的示例 - 只要确保您遵循这个概念就可以了。
关于c# Parallel.For 和 UI 更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4783109/