2015-06-13 181 views
0

我已經創建了一個應用程序,它可以生成多個後臺工作人員。前提是我有一個datagridview,並且我有一個按鈕列。停止/取消BackgroundWorker

按鈕列將其名稱更改爲開始和停止。

以下是按鈕的代碼單擊

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(方法),然後通過點擊按鈕功能呢?

回答

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(); 

您也應該檢查的CancellationPending標誌在你的DoWork方法(從here): 此屬性是指由工作線程,使用這應該定期檢查CancellationPending並在設置爲true時中止後臺操作。

CancelAsync不能真正阻止你的工作,因爲它不知道你在做什麼,所以你必須自己檢查標誌並取消你自己的工作。這也允許您在結束工作人員之前執行您可能需要的任何清理操作。 這允許您檢查工作者的取消標誌。

編輯:爲避免你的進程阻塞工人,而不是proc.WaitForExit()使用方法:

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

我試過了,在調試器中,我可以看到它正在執行工作[key] .CancelAsyc();但RunWorkerCompleted操作未運行。這是爲什麼? – progrAmmar

+0

正如我所說的,您還應該檢查DoWork方法中的CancellationPending標誌。當這個標記爲真時,你需要用你的道路方法自己停止工作。有關更多詳細信息,請轉至https://msdn.microsoft.com/zh-CN/library/system.componentmodel.backgroundworker.cancellationpending (v=vs.110).aspx。 – Alioza

+0

明白了,我把它放在那裏,但我認爲這個問題是由於這個 proc.WaitForExit(); 因此,雖然工作人員可能已經取消了同步,但進程仍在運行,我想殺死該進程。一旦我殺了它,我就進入了runco​​mplete事件。 如何殺死WaitForExit中的進程? – progrAmmar

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(); 
} 
+0

你缺少一個CancelAsync()應該在DoWork的事件處理,以可能的工作。 –

+0

確實:)沒有檢查他的DoWork :) – user3601887