我正在开发一个WinForms应用程序,该应用程序正在启动几个后台工作人员。当一个后台工作人员完成时,如果结果失败,它将通过ShowDialog(this)方法显示一个对话框。问题是当多个后台工作人员结果失败时,它将同时显示多个对话框。我不认为这是可能的,但显然是的。我读了一些关于Message循环的内容,看起来即使打开了一个对话框,消息循环仍然在处理消息,这意味着,即使已经打开了一个对话框,runworkercompleted也会被调用。我想我可以在对话框上使用一个"lock (myObject)“,但似乎没有,我猜是因为每次都是同一个线程调用锁。
那么,解决这个问题的适当方法是什么呢?我很想用这样的标志和循环:
public bool dialogOpen = false;
public bool cancelMessages = false;
public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
while (dialogOpen) {}
if (cancelMessages) return;
dialogOpen = true;
MyDialog dlg = new MyDialog("Something went wrong.");
if (dlg.ShowDialog(this) == DialogResult.Cancel) cancelMessages = true;
dialogOpen = false;
}这样能行吗?这会不会导致其他不好的事情发生?(这会阻止消息循环吗?)
发布于 2014-02-20 17:05:32
您必须从BackgroundWorker()工作线程内部询问用户,DoWork()方法本身。一个简单的lock语句将阻止它们试图显示多个对话框。您可以使用Invoke()来正确显示主UI线程上的对话框。
下面是一个简化的示例:
private void button1_Click(object sender, EventArgs e)
{
for(int i = 1; i <=5; i++)
{
BackgroundWorker x = new BackgroundWorker();
x.DoWork += x_DoWork;
x.RunWorkerCompleted += x_RunWorkerCompleted;
x.RunWorkerAsync();
}
}
private bool cancelMessages = false;
private Object dialogLock = new object();
void x_DoWork(object sender, DoWorkEventArgs e)
{
// ... some work ...
System.Threading.Thread.Sleep(5000); // five seconds worth of "work"
if (true) // some error occurred
{
lock (dialogLock) // only one worker thread can enter here at a time
{
if (!cancelMessages) // if error messages haven't been turned off, ask the user
{
// ask the user on the main UI thread:
// *Invoke() is SYNCHRONOUS, so code won't go continue until after "dlg" is dismissed
this.Invoke((MethodInvoker)delegate() {
MyDialog dlg = new MyDialog("Something went wrong.");
if (dlg.ShowDialog(this) == DialogResult.Cancel)
cancelMessages = true;
});
}
}
}
}
public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("RunWorkerCompleted");
}发布于 2014-02-20 16:13:24
ShowDialog没有阻塞任何东西(所以所有的事件都会继续触发),只有在该方法中显示模态形式的代码才会等待它关闭。
你关于变量的想法几乎是好的。如果您只想为所有员工显示一个对话框,那么(不是很完美的解决方案)
public bool dialogOpen = false;
public void x_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// check
if(dialogOpen)
return;
dialogOpen = true;
(new MyDialog("Something went wrong.")).ShowDialog(this);
dialogOpen = false;
}这里的问题是争用条件,如果有几个工作人员在dialogOpen尚未被任何一个设置为true时检查它,则会发生争用条件。如果您希望它是完美的,那么使用ManualResetEvent代替。
但是,您似乎需要的是所有工作人员将显示的错误,但同时只需要一个错误。这更难,您的解决方案是错误的,因为您正在阻塞UI线程本身。最简单的方法是阻止(阻止)工人自己完成工作,如果其中一个人正在使用对话框(和以前一样,使用ManualResetEvent)。
如果你对密码有困难,我明天就帮你。
发布于 2014-02-20 17:14:53
最后,我用消息队列进行了bool检查。这似乎起作用了。如果像Sinatr所建议的那样存在争用条件,那么我不确定为什么,据我所理解,这都是在UI线程上进行的。
这是我为让它正常工作而做的一个切碎的版本。
public List<BGWOProcess> messageQueue = new List<BGWOProcess>();
public static bool DialogOpen = false;
public static bool CancelPending = false;
public void loginProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BGWOProcess result = (BGWOProcess)e.Result;
if (!result.Result)
{
if (CancelPending) return;
if (!DialogOpen) DialogOpen = true;
else
{
messageQueue.Add(result);
return;
}
try
{
processFailedMessage(result);
}
catch (Exception) { }
DialogOpen = false;
}
else
{
//...
}
}
public void processFailedMessage(BGWOProcess result)
{
MyMessage msg = new MyMessage("The process " + result.Label + " failed: " + result.Error + " Retry?");
if (msg.ShowDialog(this) == System.Windows.Forms.DialogResult.Yes)
{
// Retry request.
Queue(result.func, result.Label, result.progressIncrement);
if (messageQueue.Count > 0)
{
BGWOProcess nextMessage = messageQueue[0];
messageQueue.Remove(nextMessage);
processFailedMessage(nextMessage);
}
}
else
{
r = false;
CancelPending = true;
// Fail.
DialogResult = System.Windows.Forms.DialogResult.Abort;
}
}https://stackoverflow.com/questions/21912593
复制相似问题