停止/取消BackgroundWorker

时间:2015-06-13 04:22:00

标签: c# multithreading winforms datagridview

我创建了一个生成多个后台工作程序的应用程序。前提是我有一个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);

    }

最后这是工作流程

 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()的方法并将按钮单击功能传递给它吗?

2 个答案:

答案 0 :(得分:1)

你必须让你的工人停下来。将它们保存在字典中,并将该行的标识符作为键:

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

然后,对于每个worker,您需要为RunWorkerCompleted事件添加处理程序,在该事件中从字典中删除worker:

 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实际上无法阻止你的工作,因为它不知道你在做什么,所以你必须自己检查标志并取消你自己的工作。这也允许您在结束工作程序之前执行可能需要的任何清理操作。 这允许您检查工作人员的取消标志。

编辑:为避免您的进程阻止worker而不是proc.WaitForExit(),请使用:

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

答案 1 :(得分:0)

你只是在杀死进程 - 而不是停止后台工作。 您必须使用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();
}