我正在开发一个允许创建其他表单的应用程序的插件。一些插件是严格意义上的“随时随地运行”的工作者,但其中一些插件具有用于进一步控制的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);
}
}
}
我觉得我可能遗漏了一个小细节,并希望额外的一双眼睛。
为什么这样做?你已经有了一个完美的STA线程,你的GUI已经在运行。为什么在创建一个不同的线程来接管GUI的处理时将它与一些长时间运行的流程捆绑在一起来滥用它?请注意,只要使用正确的Control实例调用方法,Control.Invoke/BeginInvoke就可以正常工作;如果您希望在新的UI线程上调用目标委托,则需要使用该线程拥有的Control实例,而不是您的主UI窗体实例。 – 2014-10-17 01:12:05
我更新了代码。主线程并不总是有一个GUI,它也订阅PluginManager提供的许多事件。 – Parazuce 2014-10-17 01:30:29
两个在你的问题中至少有两个严重缺陷:1)没有问题。你知道工作中存在一些错误,但没有具体描述你期望发生什么,也没有具体描述发生什么,也不同于此。和2),代码示例不完整,充满了其他人无法访问的内容,因为它应该是一个简洁但完整的代码示例,可以可靠地重现问题。 – 2014-10-17 01:39:32