C# 从Parallel.ForEach更新UI

C# 从Parallel.ForEach更新UI,c#,winforms,parallel.foreach,C#,Winforms,Parallel.foreach,我正在尝试更新Parallel.ForEach中的进度条,应用程序似乎冻结了 尽管我已经像其他帖子建议的那样尝试使用Invoke解决方案,但我仍然有问题 这是我的代码的简化版本: public partial class Form1 : Form { event EventHandler ReportProgress; class ProgressEvent : EventArgs { public int Total { get; set; }

我正在尝试更新Parallel.ForEach中的进度条,应用程序似乎冻结了

尽管我已经像其他帖子建议的那样尝试使用
Invoke
解决方案,但我仍然有问题

这是我的代码的简化版本:

public partial class Form1 : Form
{
    event EventHandler ReportProgress;

    class ProgressEvent : EventArgs
    {
        public int Total { get; set; }
        public int Current { get; set; }
    }

    public Form1()
    {
        InitializeComponent();
        ReportProgress += Form1_ReportProgress;
    }

    private void Form1_ReportProgress( object sender, EventArgs e )
    {
        var progress = e as ProgressEvent;
        if ( InvokeRequired )
        {
            this.Invoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );
        }
    }

    private void buttonStart_Click( object sender, EventArgs e )
    {
        // Create a list with seed to be used later
        List<int> values = new List<int>() { };
        values.AddRange( Enumerable.Range( 1, 15 ) );

        var total = values.Count();
        var current = 0;

        Parallel.ForEach( values, v =>
        {
            Interlocked.Increment( ref current );

            var r = new Random( v );
            // Just sleep a little
            var sleep = r.Next( 10, 1000 );
            Thread.Sleep( sleep );

            // Update the progress bar
            ReportProgress( null, new ProgressEvent() { Current = current, Total = total } );
        }
        );

        MessageBox.Show( "Done " );
    }
}
公共部分类表单1:表单
{
事件处理程序报告进度;
类ProgressEvent:EventArgs
{
公共整数总计{get;set;}
公共int当前{get;set;}
}
公共表格1()
{
初始化组件();
ReportProgress+=Form1_ReportProgress;
}
私有void Form1_ReportProgress(对象发送方,事件参数e)
{
var progress=e作为ProgressEvent;
如果(需要调用)
{
调用(新操作(()=>{progressBar.max=progress.Total;progressBar.Value=progress.Current;}));
}
}
私有无效按钮开始单击(对象发送者,事件参数e)
{
//创建一个包含种子的列表,以便以后使用
列表值=新列表(){};
AddRange(可枚举的范围(1,15));
var total=values.Count();
无功电流=0;
Parallel.ForEach(值,v=>
{
联锁增量(参考电流);
var r=新的随机变量(v);
//睡一会儿就行了
var sleep=r.Next(101000);
睡眠;
//更新进度条
ReportProgress(null,new ProgressEvent(){Current=Current,Total=Total});
}
);
MessageBox.Show(“完成”);
}
}

应用程序似乎挂起,消息框未显示,而如果我删除事件生成(
ReportProgress
),一切都会正常工作,但显然进度条根本没有更新。

我编辑了您的代码。若你们想让屏幕不冻结,可能最简单的方法就是使用后台工作程序来执行操作

当您使用后台工作程序时,UI线程不会被阻塞。因此,用户仍然可以与UI交互

public partial class Form1 : Form
{
    event EventHandler ReportProgress;

    class ProgressEvent : EventArgs
    {
        public int Total { get; set; }
        public int Current { get; set; }
    }

    public Form1()
    {
        InitializeComponent();
        ReportProgress += Form1_ReportProgress;
    }

    private void Form1_ReportProgress(object sender, EventArgs e)
    {
        var progress = e as ProgressEvent;
        this.Invoke(new Action(() =>
                               {
                                   progressBar.Maximum = progress.Total;
                                   progressBar.Value = progress.Current;
                               }));
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // Collect data from UI, I use list for example
        List<int> values = new List<int>() { };
        values.AddRange(Enumerable.Range(1, 100));

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += BackgroundWorkerOnDoWork;
        backgroundWorker.RunWorkerAsync(values);
    }

    private void BackgroundWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        var values = (IEnumerable<int>) doWorkEventArgs.Argument; // the 'argument' parameter resurfaces here

        var total = values.Count();
        var current = 0;

        Parallel.ForEach(values, v =>
                                 {
                                     Interlocked.Increment(ref current);

                                     var r = new Random(v);
                                     // Just sleep a little
                                     var sleep = r.Next(10, 1000);
                                     Thread.Sleep(sleep);

                                     // Update the progress bar
                                     ReportProgress(null, new ProgressEvent() {Current = current, Total = total});
                                 }
                        );

        MessageBox.Show("Done ");
    }
}
公共部分类表单1:表单
{
事件处理程序报告进度;
类ProgressEvent:EventArgs
{
公共整数总计{get;set;}
公共int当前{get;set;}
}
公共表格1()
{
初始化组件();
ReportProgress+=Form1_ReportProgress;
}
私有void Form1_ReportProgress(对象发送方,事件参数e)
{
var progress=e作为ProgressEvent;
this.Invoke(新操作(()=>
{
progressBar.Maximum=进度.总计;
progressBar.Value=progress.Current;
}));
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
//从UI收集数据,例如,我使用列表
列表值=新列表(){};
values.AddRange(可枚举的范围(1100));
BackgroundWorker BackgroundWorker=新的BackgroundWorker();
backgroundWorker.DoWork+=BackgroundWorkerOnDoWork;
backgroundWorker.RunWorkerAsync(值);
}
私有void BackgroundWorkRondowork(对象发送方,DoWorkEventArgs DoWorkEventArgs)
{
var values=(IEnumerable)doWorkEventArgs.Argument;//此处重新出现'Argument'参数
var total=values.Count();
无功电流=0;
Parallel.ForEach(值,v=>
{
联锁增量(参考电流);
var r=新的随机变量(v);
//睡一会儿就行了
var sleep=r.Next(101000);
睡眠;
//更新进度条
ReportProgress(null,new ProgressEvent(){Current=Current,Total=Total});
}
);
MessageBox.Show(“完成”);
}
}
您可以更改:

this.Invoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );
致:

这将在异步线程上执行该操作。

看看这个:
this.BeginInvoke( new Action( () => { progressBar.Maximum = progress.Total; progressBar.Value = progress.Current; } ) );