首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ShowDialog in BackgroundWorker RunWorkerCompleted

ShowDialog in BackgroundWorker RunWorkerCompleted
EN

Stack Overflow用户
提问于 2014-02-20 15:41:57
回答 3查看 1.5K关注 0票数 1

我正在开发一个WinForms应用程序,该应用程序正在启动几个后台工作人员。当一个后台工作人员完成时,如果结果失败,它将通过ShowDialog(this)方法显示一个对话框。问题是当多个后台工作人员结果失败时,它将同时显示多个对话框。我不认为这是可能的,但显然是的。我读了一些关于Message循环的内容,看起来即使打开了一个对话框,消息循环仍然在处理消息,这意味着,即使已经打开了一个对话框,runworkercompleted也会被调用。我想我可以在对话框上使用一个"lock (myObject)“,但似乎没有,我猜是因为每次都是同一个线程调用锁。

那么,解决这个问题的适当方法是什么呢?我很想用这样的标志和循环:

代码语言:javascript
复制
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;
}

这样能行吗?这会不会导致其他不好的事情发生?(这会阻止消息循环吗?)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-02-20 17:05:32

您必须从BackgroundWorker()工作线程内部询问用户,DoWork()方法本身。一个简单的lock语句将阻止它们试图显示多个对话框。您可以使用Invoke()来正确显示主UI线程上的对话框。

下面是一个简化的示例:

代码语言:javascript
复制
    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");
    }
票数 1
EN

Stack Overflow用户

发布于 2014-02-20 16:13:24

ShowDialog没有阻塞任何东西(所以所有的事件都会继续触发),只有在该方法中显示模态形式的代码才会等待它关闭。

你关于变量的想法几乎是好的。如果您只想为所有员工显示一个对话框,那么(不是很完美的解决方案)

代码语言:javascript
复制
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)。

如果你对密码有困难,我明天就帮你。

票数 0
EN

Stack Overflow用户

发布于 2014-02-20 17:14:53

最后,我用消息队列进行了bool检查。这似乎起作用了。如果像Sinatr所建议的那样存在争用条件,那么我不确定为什么,据我所理解,这都是在UI线程上进行的。

这是我为让它正常工作而做的一个切碎的版本。

代码语言:javascript
复制
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;
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21912593

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档