winforms:使用parallel.foreach更新进度

时间:2018-06-28 22:38:01

标签: c# multithreading winforms parallel.foreach

我还没有看到与我的问题有关的任何帖子,因此,如果我已经发布了一个问题,我深表歉意。

我有一个Windows窗体程序c#,用于检查库存并进行分析。主窗体通过新线程和ShowDialog启动另一个窗体。在加载时,它正在运行parallel.foreach。在该parallel.foreach中,我想显示主要表单上的进度。

我遇到了跨线程问题,并添加了invoke,尽管它似乎不是线程安全的,因为它似乎在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。这对我有用。仍然想弄清楚为什么如果我想要对主要组件进行更新,它仍然会锁定在最后(我一直都读异步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)));
        }
    }

3 个答案:

答案 0 :(得分:1)

Parallel.ForEach是阻止呼叫;它也在调用线程上运行委托。因此,UI在完成之前无法更新。

相反,您应该将awaitTask.WhenAll(如果要进行异步工作)或Task.Run(() => Parallel.ForEach(...))(如果受CPU限制)一起使用,以使UI线程空闲并能够更新。

答案 1 :(得分:0)

您可以对此对象使用Async Await功能...此链接对您更有用...

PictureBox animation freezing while Task running

答案 2 :(得分:-1)

根据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);