2014-10-17 35 views
-3

我正在开发一个允许创建其他表单的应用程序的插件。一些插件是严格意义上的“随时随地运行”的工作者,但其中一些插件具有用于进一步控制的UI。该插件公开了一个API(通过.dll),它应该被子类化(通过它们的Core类)。它有一个初始化插件的方法,以及一个关闭插件的方法。主线程上的工作者,其他线程上的UI。我究竟做错了什么?

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

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

为什么在我致电form1.UpdateScanningProgress(i)时导致GUI线程被阻塞?在下面的例子中,似乎this.InvokeRequired总是为false,即使我在另一个线程中运行表单。

这里是“主”代码。请注意,插件信息已被删除。我想尽可能地简单:

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

为什么这样做?你已经有了一个完美的STA线程,你的GUI已经在运行。为什么在创建一个不同的线程来接管GUI的处理时将它与一些长时间运行的流程捆绑在一起来滥用它?请注意,只要使用正确的Control实例调用方法,Control.Invoke/BeginInvoke就可以正常工作;如果您希望在新的UI线程上调用目标委托,则需要使用该线程拥有的Control实例,而不是您的主UI窗体实例。 – 2014-10-17 01:12:05

+0

我更新了代码。主线程并不总是有一个GUI,它也订阅PluginManager提供的许多事件。 – Parazuce 2014-10-17 01:30:29

+2

两个在你的问题中至少有两个严重缺陷:1)没有问题。你知道工作中存在一些错误,但没有具体描述你期望发生什么,也没有具体描述发生什么,也不同于此。和2),代码示例不完整,充满了其他人无法访问的内容,因为它应该是一个简洁但完整的代码示例,可以可靠地重现问题。 – 2014-10-17 01:39:32

回答

0

我对“this.InvokeRequired”实际上返回false表示怀疑。但无论如何,你都在调用错误的“BeginInvoke”方法。

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

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

现在编写代码的方式,您将进入调用UpdateScanningProgress方法的无限循环,并且在任何时候都不会执行更新进度UI。可能GUI线程不会被阻塞......它从来没有被要求做任何有趣的事情。

个人而言,我根本不使用“InvokeRequired”。当它返回false时,使用Invoke或甚至BeginInvoke都是无害的,没有它的代码就更简单。所以我总是直接调用Invoke/BeginInvoke。

试试这个:

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