winforms:使用 parallel.foreach 更新进度
winforms: updating progress with a parallel.foreach
我还没有看到任何 post 与我的问题有关,所以如果我 post 已经提出了一个问题,我深表歉意。
我有一个 windows 表单程序,c#,可以检查股票并进行分析。主窗体通过新线程和 ShowDialog 启动另一个窗体。在加载时,它正在 运行 宁 parallel.foreach。在那个 parallel.foreach 中,我想在主窗体上显示进度。
我已经 运行 解决了跨线程问题,并添加了调用,尽管它似乎不是线程安全的,因为它似乎在 parallel.foreach 结束时死锁.我试过代表、活动,但没有成功。帮助我欧比旺斯,你是我唯一的希望!
精简版:
主窗体
private void button1_Click(object sender, EventArgs e)
{
YearLows yearLows = new YearLows();
Thread yearLowsThread = new Thread(() => StartYearLows(yearLows));
yearLowsThread.Start();
btnGetYearLows.Enabled = false;
}
private void StartYearLows(YearLows yearLows)
{
yearLows.ShowDialog();
}
public void UpdateProgress(string text)
{
lblProgress.Text = text;
}
第二种形式的对话
public partial class YearLows : Form
{
private void YearLows_Load(object sender, EventArgs e)
{
// work
Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
});
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => frmMain.UpdateProgress(text)));
}
}
更新 1:
如果我将进度更新标签移动到子窗体,看起来我正在获取所有进度更新。我必须从 Load 事件移动到 Shown 事件,以便呈现表单,以便用户可以看到进度更新。不过,我不得不遵循 SLaks 的建议 运行 Task.Run(() => Parallel.ForEach。这对我有用。仍然想弄清楚为什么它仍然会在最后锁定,如果我想要主窗体上的进度更新。(我一直读到 async void 很糟糕,但我想在 winforms 中这些定义的方法签名中没有办法解决这个问题)
public partial class YearLows : Form
{
private async void YearLows_Shown(object sender, EventArgs e)
{
await AnalyzeStocks();
}
private async Task AnalyzeStocks(object sender, EventArgs e)
{
// work
await Task.Run(() => Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
}));
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => lblProgress.UpdateProgress(text)));
}
}
Parallel.ForEach
是阻塞调用;它也在调用线程上运行委托。因此,UI 在完成之前无法更新。
相反,您应该将 await
与 Task.WhenAll
(如果您正在进行异步工作)或 Task.Run(() => Parallel.ForEach(...))
(如果它是 CPU-绑定)一起使用,这样您让 UI 线程空闲并能够更新。
您可以为这个程序使用 Async Await 函数...这个 link 可能对您更有用...
根据 SLaks 的回答,使用 Task.Run 的示例,以及 UI 更新
var tasks = new List<Task>();
foreach (var result in results)
{
tasks.Add(Task.Run(async () => {
// DO WORK
Dispatcher.Invoke(() =>
{
// UPDATE THE UI, I.E. ProgressBar.Value++;
});
}));
}
await Task.WhenAll(tasks);
我还没有看到任何 post 与我的问题有关,所以如果我 post 已经提出了一个问题,我深表歉意。
我有一个 windows 表单程序,c#,可以检查股票并进行分析。主窗体通过新线程和 ShowDialog 启动另一个窗体。在加载时,它正在 运行 宁 parallel.foreach。在那个 parallel.foreach 中,我想在主窗体上显示进度。
我已经 运行 解决了跨线程问题,并添加了调用,尽管它似乎不是线程安全的,因为它似乎在 parallel.foreach 结束时死锁.我试过代表、活动,但没有成功。帮助我欧比旺斯,你是我唯一的希望!
精简版:
主窗体
private void button1_Click(object sender, EventArgs e)
{
YearLows yearLows = new YearLows();
Thread yearLowsThread = new Thread(() => StartYearLows(yearLows));
yearLowsThread.Start();
btnGetYearLows.Enabled = false;
}
private void StartYearLows(YearLows yearLows)
{
yearLows.ShowDialog();
}
public void UpdateProgress(string text)
{
lblProgress.Text = text;
}
第二种形式的对话
public partial class YearLows : Form
{
private void YearLows_Load(object sender, EventArgs e)
{
// work
Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
});
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => frmMain.UpdateProgress(text)));
}
}
更新 1: 如果我将进度更新标签移动到子窗体,看起来我正在获取所有进度更新。我必须从 Load 事件移动到 Shown 事件,以便呈现表单,以便用户可以看到进度更新。不过,我不得不遵循 SLaks 的建议 运行 Task.Run(() => Parallel.ForEach。这对我有用。仍然想弄清楚为什么它仍然会在最后锁定,如果我想要主窗体上的进度更新。(我一直读到 async void 很糟糕,但我想在 winforms 中这些定义的方法签名中没有办法解决这个问题)
public partial class YearLows : Form
{
private async void YearLows_Shown(object sender, EventArgs e)
{
await AnalyzeStocks();
}
private async Task AnalyzeStocks(object sender, EventArgs e)
{
// work
await Task.Run(() => Parallel.ForEach(responseStocks, new ParallelOptions { MaxDegreeOfParallelism = MaxThreads }, x =>
{
// more work
Interlocked.Increment(stocksProcessed);
UpdateProgress($"{stocksProcessed} / {stocksTotal} Researched");
}));
}
private void UpdateProgress(string text)
{
Invoke(new Action(() => lblProgress.UpdateProgress(text)));
}
}
Parallel.ForEach
是阻塞调用;它也在调用线程上运行委托。因此,UI 在完成之前无法更新。
相反,您应该将 await
与 Task.WhenAll
(如果您正在进行异步工作)或 Task.Run(() => Parallel.ForEach(...))
(如果它是 CPU-绑定)一起使用,这样您让 UI 线程空闲并能够更新。
您可以为这个程序使用 Async Await 函数...这个 link 可能对您更有用...
根据 SLaks 的回答,使用 Task.Run 的示例,以及 UI 更新
var tasks = new List<Task>();
foreach (var result in results)
{
tasks.Add(Task.Run(async () => {
// DO WORK
Dispatcher.Invoke(() =>
{
// UPDATE THE UI, I.E. ProgressBar.Value++;
});
}));
}
await Task.WhenAll(tasks);