在 Parallel.ForEach 循环中实例化用户控件

本文关键字:实例化 用户 控件 循环 Parallel ForEach | 更新日期: 2023-09-27 18:37:28

在我的应用程序中,我有一个新的Windows窗体(称为监视器),它基本上由一个大显示器(这是一个自定义用户控件)组成,它本身由许多排列在网格中的小图表(也是用户控件)组成。 当我实例化监视器窗体时,将创建一个新的显示对象,然后继续创建一堆图表控件。 然后将图表控件添加到"display"的控件集合(Controls.Add(chart)),完成显示的加载,然后将显示添加到"监视器"的控件集合中,并显示窗体。

我遇到的问题是每个图表控件加载大约需要 0.5 秒,而我们可以在一个显示对象中拥有大约 75 个图表控件。 因此,我想并行创建这些图表,以加快加载时间。 我利用 TPL 方法Parallel.ForEach来实现这一点。

我的代码看起来像这样,在"display"对象的构造函数中:

public class DisplayControl : UserControl
{
    public DisplayControl()
    {
        var chartNames = new string[] { "chart1", "chart2", ... };
        var builtCharts = new ConcurrentBag<Chart>();
        // Create the chart objects in parallel
        Parallel.ForEach(chartNames, name =>
            {
                // The chart constructor creates the chart itself, which is a 
                // custom user control object (inherits UserControl), composed  
                // of other various WinForm controls like labels, buttons, etc.
                var chart = new Chart();
                chart.Name = name;
                builtCharts.Add(chart);
            }
        );
        // Clean up the charts and add them to "display's" control collection
        foreach(var chart in builtCharts)
        {
            // Do some unimportant modifications to the chart, synchronously
            ...
            this.Controls.Add(chart);
        }
    }
}
DisplayControl

构造函数由主窗体 Monitor 在主线程上调用,并且 DisplayControl 实例本身被添加到 Monitor 的 ControlCollection 中。 然后使用 Monitor.Show() 显示监视器,这只是Form类的Show方法。

我遇到的主要问题是 DisplayControl 构造函数偶尔会在this.Controls.Add行抛出InvalidOperationException,引用以下内容:

System.InvalidOperationException: Cross-thread operation not valid: Control 'panel' accessed from a thread other than the thread it was created on.

我应该指出,panel只是一个面板 WinForms 控件,它是在 Chart() 构造函数中创建的。

最后,代码似乎在大约 90% 的时间内工作正常,这些错误似乎是随机的。 如果我收到错误,我通常可以立即重新运行代码,它就会起作用。

我的目标是消除此错误并使所有线程安全。

在 Parallel.ForEach 循环中实例化用户控件

所有关于System.Windows.Forms的操作都必须在主线程中执行。关于这一点有很多问题;就像这里。因此,如果您只是在并行循环中创建图表,那么最好将其保留为普通foreach