Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# BackgroundWorker Runworker Completed中的ShowDialog_C#_Winforms_Backgroundworker - Fatal编程技术网

C# BackgroundWorker Runworker Completed中的ShowDialog

C# BackgroundWorker Runworker Completed中的ShowDialog,c#,winforms,backgroundworker,C#,Winforms,Backgroundworker,我正在开发一个WinForms应用程序,它将启动几个后台工作程序。当一个后台工作程序完成时,如果结果是失败,它将通过ShowDialog(this)方法显示一个对话框。问题是当多个后台工作程序结果失败时,它将同时显示多个对话框。我不认为这是可能的,但显然是。我读了一些关于消息循环的内容,似乎即使对话框打开,消息循环仍在处理消息,这意味着即使对话框已经打开,也会调用runworkercompleted。我原以为我可以在对话框上使用“锁(myObject)”,但现在看来不行,我猜是因为同一个线程每次

我正在开发一个WinForms应用程序,它将启动几个后台工作程序。当一个后台工作程序完成时,如果结果是失败,它将通过
ShowDialog(this)
方法显示一个对话框。问题是当多个后台工作程序结果失败时,它将同时显示多个对话框。我不认为这是可能的,但显然是。我读了一些关于消息循环的内容,似乎即使对话框打开,消息循环仍在处理消息,这意味着即使对话框已经打开,也会调用runworkercompleted。我原以为我可以在对话框上使用“锁(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;
}

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

ShowDialog
不会阻止任何内容(因此所有事件都将持续触发),只有该方法中显示模态形式的代码才会等待它关闭

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

这里的问题是竞态条件,当两个工人都未将其设置为true时,如果有几个工人将检查
dialogOpen
,就会出现竞态条件。如果您希望它是完美的,则使用
ManualResetEvent

但是,您似乎希望所有工作人员都显示错误,但一次只显示一个错误。更难的是,您的解决方案是错误的,因为您正在阻止UI线程本身。最简单的方法是防止(阻止)工作人员自己完成,如果其中一人正在使用dialog(与以前一样,使用
ManualResetEvent


如果您在代码方面有困难,我明天会帮助您。

您必须从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");
    }
private void按钮1\u单击(对象发送者,事件参数e)
{

对于(int i=1;i我最终使用一个消息队列执行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;
    }
}
public List messageQueue=new List();
公共静态bool DialogOpen=false;
公共静态bool CancelPending=false;
public void loginProcess\u RunWorkerCompleted(对象发送方,runworkercompletedeventarge)
{
BGWOProcess result=(BGWOProcess)e.result;
如果(!result.result)
{
如果(取消挂起)返回;
如果(!DialogOpen)DialogOpen=true;
其他的
{
messageQueue.Add(结果);
返回;
}
尝试
{
processFailedMessage(结果);
}
捕获(异常){}
DialogOpen=false;
}
其他的
{
//...
}
}
public void processFailedMessage(BGWOProcess结果)
{
MyMessage msg=newmymessage(“进程”+result.Label+”失败:“+result.Error+”重试?”);
if(msg.ShowDialog(this)=System.Windows.Forms.DialogResult.Yes)
{
//重试请求。
队列(result.func、result.Label、result.progressIncrement);
如果(messageQueue.Count>0)
{
BGWOProcess nextMessage=messageQueue[0];
messageQueue.Remove(nextMessage);
processFailedMessage(下一条消息);
}
}
其他的
{
r=假;
CancelPending=true;
//失败。
DialogResult=System.Windows.Forms.DialogResult.Abort;
}
}

我刚刚尝试了循环,它阻止了一切。(对话框不起作用。)我可能需要建立一个消息队列。为什么不只跟踪后台线程完成的数量与声明的数量,当计数下降到0时,只有在出现任何失败时才显示消息框?是的,这是很有可能的。消息框不会阻止Windows消息的正常流动。它只会禁用其余的窗口在应用程序中,阻止用户生成输入消息。不使用消息框,而是使用更类似于显示消息列表的窗口可能是合适的。这也可以防止用户在愉快地单击时意外关闭框。我想我不明白这是如何工作的。如果消息s发送到RunWorkerCompleted发生在主UI线程上,那么为什么根据Sinatr的回答会有竞态条件。我已经尝试实现布尔检查。如果所有的都在同一线程上处理,则不应该有竞态条件…消息框不是这里的选项。对话框更多的是“继续/忽略”或“取消/退出”选项。因此,如果用户点击“取消/退出”,我们不关心其余的对话框。我最终选择了另一条路线,但这似乎也会起作用。
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;
    }
}