主线程的工作者,在其他螺纹的UI。我究竟做错了什么?

时间:2014-10-17 00:49:31

标签: c# .net multithreading winforms

我正在为应用程序开发一个插件,允许创建其他表单。一些插件严格来说是“一劳永逸”的工作者,但有些插件具有进一步控制的UI。该插件公开了一个API(通过.dll),该API将被子类化(通过他们的Core类)。它有一个初始化插件的方法,以及一个关闭插件的方法。

我想要做的是在主线程上有工作线程,在另一个线程上有UI线程,我可以发送事件以及从(开始,停止等)接收控制事件

我当前的实现已成功更新UI;遗憾的是,BeginInvoke是没有用的,因为表单正在我的主线程上初始化,即使它被实例化并显示在子线程中。

为什么在调用form1.UpdateScanningProgress(i)时会导致GUI线程被阻止?看来this.InvokeRequired在下面的例子中总是假的,即使我在另一个线程中运行该表单。

这是“主要”代码。请注意,插件信息已被删除。我想尽可能基本:

namespace TestBed
{
    class TestBedManager : PluginManager
    {
        Form1 form1;

        void UIInitialized(object sender, EventArgs e)
        {

        }

        void BeginScan(object sender, EventArgs e)
        {
            for (int i = 0; i <= 100; ++i)
            {
                Thread.Sleep(150);
                form1.UpdateScanningProgess(i);
            }

            Thread.Sleep(1000);
        }

        // Parent calls when plugin starts
        public void PluginStart()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            form1 = new Form1();
            form1.Initialized += Initialized;
            form1.BeginScan += BeginScan;

            Thread thread = new Thread(() =>
            {
                Application.Run(form1);
            });

            thread.Start();

            // Here is where I'd start my real work instead of simply joining
            thread.Join();
        }

        // Parent calls when plugin needs to stop
        public void PluginStop()
        {
        }
    }
}

这是表格代码:

namespace TestBed
{
    public partial class Form1 : Form
    {
        public event EventHandler Initialized;
        public event EventHandler BeginScan;

        public Form1()
        {
            InitializeComponent();
        }

        delegate void UpdateScanningProgressDelegate(int progressPercentage);

        public void UpdateScanningProgress(int progressPercentage)
        {
            if (this.InvokeRequired)
            {
                UpdateScanningProgressDelegate updateScanningProgress = new UpdateScanningProgressDelegate(UpdateScanningProgress);
                updateScanningProgress.BeginInvoke(progressPercentage, null, null);
            }
            else
            {
                this.scanProgressBar.Value = progressPercentage;
            }
        }

        delegate void StopScanningDelegate();

        public void StopScanning()
        {
            if (this.InvokeRequired)
            {
                StopScanningDelegate stopScanning = new StopScanningDelegate(StopScanning);
                stopScanning.BeginInvoke(null, null);
            }
            else
            {
                this.scanProgressBar.Value = 0;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if (Initialized != null)
                Initialized(this, EventArgs.Empty);
        }

        private void scanButton_Click(object sender, EventArgs e)
        {
            if (BeginScan != null)
                BeginScan(this, EventArgs.Empty);
        }
    }
}

我觉得我可能错过了一个小细节,并会欣赏一双额外的眼睛。

1 个答案:

答案 0 :(得分:0)

我怀疑&#34; this.InvokeRequired&#34;实际上是假的。但无论如何,你是在说错了&#34; BeginInvoke&#34;方法

您应该调用Control.Invoke或Control.BeginInvoke。但是,您正在调用编译器生成的Delegate.BeginInvoke方法。

Control.Invoke / BeginInvoke方法导致在控件的拥有线程上调用给定的委托实例。 Delegate.BeginInvoke方法只是将委托的调用排队到ThreadPool。

现在编写代码的方式,您将进入一个无休止的调用UpdateScanningProgress方法的循环,并且在任何时候都无法实际更新进度UI。可能GUI线程根本没有被阻止......它从来没有被要求做任何有趣的事情。

就个人而言,我不会使用&#34; InvokeRequired&#34;一点都不当它返回false时使用Invoke甚至BeginInvoke是无害的,并且没有它时代码更简单。所以我总是直接调用Invoke / BeginInvoke。

试试这个:

public void UpdateScanningProgress(int progressPercentage)
{
    this.BeginInvoke((MethodInvoker)(() => this.scanProgressBar.Value = progressPercentage));
}