我创建了一个生成多个后台工作程序的应用程序。前提是我有一个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()的方法并将按钮单击功能传递给它吗?
答案 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();
}