【问题标题】:Stopping / cancelling BackgroundWorker停止/取消 BackgroundWorker
【发布时间】:2015-06-13 04:22:00
【问题描述】:

我创建了一个生成多个后台工作人员的应用程序。前提是我有一个datagridview,并且有一个按钮列。

按钮列将其名称更改为“开始”和“停止”。

以下是按钮点击的代码

private void dgConfig_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        // Ignore clicks that are not on button cells.  
        string row = e.RowIndex.ToString();
        if (e.RowIndex < 0 || e.ColumnIndex > dgConfig.Columns[1].Index) 
            return;
        if (e.ColumnIndex == 0 && dgConfig.Rows[e.RowIndex].Cells[0].Value.ToString().Equals("Start"))
        {
            dgConfig.Rows[e.RowIndex].Cells[0].Value = "Stop";
            string input = string.Empty;
            string output = string.Empty;
            Dictionary<string, string> args = GenerateCommands(e.RowIndex);

           dgConfig.Rows[e.RowIndex].Cells[0].Value.ToString());
            StartThread(e.RowIndex.ToString(), "", "", args);

        }
        else if (e.ColumnIndex == 0 && dgConfig.Rows[e.RowIndex].Cells[0].Value.ToString().Equals("Stop"))
        {
            dgConfig.Rows[e.RowIndex].Cells[0].Value = "Start";


            if (processes.Count > 0)
            {
                int procId = int.Parse(processes[e.RowIndex.ToString()]);
                Process p = Process.GetProcessById(procId);
                MessageBox.Show("Stopping Process "+p.ProcessName);
                p.Close();

                p = null;

            }
    }
}

这里是启动线程方法。

private void StartThread(string row, string input, string output, Dictionary<string, string> commands)
    {

        BackgroundWorker background = new BackgroundWorker();
        background.DoWork += new DoWorkEventHandler(bkWorker_DoWork);
        background.WorkerReportsProgress = false;
        background.WorkerSupportsCancellation = true;            
        background.RunWorkerAsync(commands);

    }

最后这是 Worker 进程

 private void bkWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Dictionary<string, string> inputs = e.Argument as Dictionary<string, string>;
        string newcmd = @"C:\mympeg.exe";

        foreach (KeyValuePair<string, string> val in inputs )
        {
            newcmd += " " + val.Key + " " + val.Value;
        }

        Process proc = new Process();
        proc.StartInfo.FileName = "cmd.exe";
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.CreateNoWindow = true;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardInput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
        proc.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
        proc.Start();
        processes.Add(row, proc.Id.ToString());
        if (txtLog.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate { 
                txtLog.AppendText("Starting Process......\r\n");
                txtLog.AppendText("Command recieved: "+newcmd+"\r\n"); 
            });
        }
        else
        {
            txtLog.AppendText("Starting Process......\r\n");
            txtLog.AppendText("Command recieved: " + newcmd + "\r\n"); 
        }
        StreamWriter cmdStreamWriter = proc.StandardInput;
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();
        cmdStreamWriter.WriteLine(newcmd);
        cmdStreamWriter.Close();
        proc.WaitForExit();
        if (txtLog.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate { txtLog.AppendText("\r\nFinished Process.......\r\n"); });
        }
        else
        {
            txtLog.AppendText("\r\nFinished Process.......\r\n");
        }
        proc.Close();
    }

所以如果我启动一个长时间运行的进程,进程就会启动,我尝试挂起线程,但这不起作用。我希望能够停止特定的后台工作人员(不是全部)。

我可以在单击该行数据对应的按钮时关闭一个后台工作人员吗?

编辑

我是否可以创建一个名为 process.WaitForEvent() 的方法并将按钮单击功能传递给它?

【问题讨论】:

    标签: c# multithreading winforms datagridview


    【解决方案1】:

    你必须留住你的工人才能阻止他们。以行的标识符作为键将它们保存在字典中:

    Dictionary<string, BackgroundWorker> workers = new Dictionary<string,BackgroundWorker>();
    

    然后,您需要为每个工作人员添加 RunWorkerCompleted 事件的处理程序,在该事件中您可以从字典中删除工作人员:

     background.RunWorkerCompleted += (s, e) => 
                {
                    var instance = (BackgroundWorker)s;
                    if (e.Cancelled)
                    {
                        //cancelled
                    }
                    else
                    {
                        //finished
                    }
                    workers.Remove(workers.FirstOrDefault(x => x.Value == (instance)).Key);
                    //announce worker removed
                };
    

    您将工作人员添加到字典中,并以行标识符作为键。 当你想停止一个工人时,你只需调用

    workers[rowId].CancelAsync();
    

    您还应该检查 DoWork 方法中的 CancellationPending 标志(来自 here): 该属性供工作线程使用,它应定期检查 CancellationPending 并在设置为 true 时中止后台操作。

    CancelAsync 实际上无法为您停止工作,因为它不知道您在做什么,因此您必须自己检查标志并取消自己的工作。这还允许您在结束工作程序之前执行您可能需要的任何清理操作。 这允许您检查工作人员的取消标志。

    编辑:为避免您的进程阻塞工作人员而不是 proc.WaitForExit(),请使用:

    while(!proc.HasExited)
    {
        if(instance.CancellationPending)
        {
            //killprocess
            break;
        }
    }
    

    【讨论】:

    • 我试过了,在调试器中我可以看到它正在执行 workers[key].CancelAsyc();但 RunWorkerCompleted 操作未运行。这是为什么呢?
    • 正如我所说,您还应该检查 DoWork 方法中的 CancellationPending 标志。当这个标志为真时,你需要在你的 dowork 方法中自己停止工作。更多详情请转至msdn.microsoft.com/en-us/library/…
    • 知道了,我把它放在那里,但我认为问题是由于这个 proc.WaitForExit();因此,虽然工作人员可能有 cancelAsync,但进程仍在运行,我想终止该进程。一旦我杀死它,我就进入了 runco​​mplete 事件。如何杀死 WaitForExit 中的进程?
    • 感谢您的帮助
    【解决方案2】:

    您只是在杀死进程 - 没有停止后台工作程序。 你必须使用 BackgroundWorker.CancelAsync()

    在你的情况下可能: - 在类级别创建字典

    private Dictionary<string, BackgroundWorker> workers= new Dictionary<string, BackgroundWorker>();
    

    - 在 StartThread 中添加后台工作者,就像

    this.workers.Add(row, background);
    

    - 停止工人使用

    BackgroundWorker worker = this.workers[e.RowIndex.ToString()];
    worker.CancelAsync();
    

    编辑:

    private void bkWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Dictionary<string, string> inputs = e.Argument as Dictionary<string, string>;
        string newcmd = @"C:\mympeg.exe";
    
        BackgrounWorker instance = sender as BackgroundWorker;
    
        if (instance == null)
        {
           e.Cancel = true;
           return;
        }
    
        Process proc = new Process();
        proc.StartInfo.FileName = "cmd.exe";
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.CreateNoWindow = true;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardInput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
        proc.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutputHandler);
        proc.Start();
        processes.Add(row, proc.Id.ToString());
        if (txtLog.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate { 
                txtLog.AppendText("Starting Process......\r\n");
                txtLog.AppendText("Command recieved: "+newcmd+"\r\n"); 
            });
        }
        else
        {
            txtLog.AppendText("Starting Process......\r\n");
            txtLog.AppendText("Command recieved: " + newcmd + "\r\n"); 
        }
        StreamWriter cmdStreamWriter = proc.StandardInput;
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();
    
        cmdStreamWriter.Write(newcmd);
    
        foreach (KeyValuePair<string, string> val in inputs )
        {
            cmdStreamWriter.Write(" " + val.Key + " " + val.Value);
    
            if(instance.CancellationPending)
            {
                break;
            }
        }
    
        cmdStreamWriter.Close();
    
        if (txtLog.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate { txtLog.AppendText("\r\nFinished Process.......\r\n"); });
        }
        else
        {
            txtLog.AppendText("\r\nFinished Process.......\r\n");
        }
        proc.Close();
    }
    

    【讨论】:

    • 您错过了应该在 DoWork 事件中处理 CancelAsync() 才能正常工作。
    • 确实:) 还没有检查他的 DoWork:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-21
    • 1970-01-01
    • 2015-01-13
    • 2015-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多