C#.net,在处理长任务时引发事件,显示一个可以有 "Pause" 和 "Continue" 继续处理的表单

C#.net, raising events while processing a long task, show a form which can have "Pause" and "Continue" to continue processing

我逐行处理文件,读取具有时间戳和相关数据的各种事件。我希望能够在进行处理时显示一个表单,我需要通过一个按钮说 interrupt EventX 来与之交互并拦截一些事件,如果按下它,它将在该事件发生时在富文本框字段中显示事件数据到达未来的某个时间。然后我可以更改一些事件数据(假设我模拟了一些条件),当我按下 "Resume" 时,它应该通过向预期订阅者引发事件以进行进一步处理来恢复处理。

所以我需要一个拦截器,当按下某个表单元素并将该数据传递给预期的订阅者时,该拦截器将成为传递机制。 我可以同步等待修改数据并按下 "Resume"

谢谢

如果您想在执行长时间 运行ning 操作时拥有响应式 GUI,则需要某种形式的多任务处理。这意味着 async/await 或许多多线程(主要是线程和 BackgroundWorker)方法中的任何一种

虽然可以添加暂停和恢复,但这样做通常需要做更多的工作,这是值得的。至少你 运行 遇到了诸如仍然持有的文件句柄或竞争条件之类的问题。通常 "cancel" 动作是 enough/better 然后是完整的 stop/resume 机制。

作为初学者,我建议您使用 BackgroundWorker。它就像通过多线程进入多任务处理一样简单。几年前我什至为它写了一个例子:

#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
    if (!bgwPrim.IsBusy)
    {
        //Prepare ProgressBar and Textbox
        int temp = (int)nudPrim.Value;
        pgbPrim.Maximum = temp;
        tbPrim.Text = "";

        //Start processing
        bgwPrim.RunWorkerAsync(temp);
    }
}

private void btnPrimCancel_Click(object sender, EventArgs e)
{
    if (bgwPrim.IsBusy)
    {
        bgwPrim.CancelAsync();
    }
}

private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
    int highestToCheck = (int)e.Argument;
    //Get a reference to the BackgroundWorker running this code
    //for Progress Updates and Cancelation checking
    BackgroundWorker thisWorker = (BackgroundWorker)sender;

    //Create the list that stores the results and is returned by DoWork
    List<int> Primes = new List<int>();


    //Check all uneven numbers between 1 and whatever the user choose as upper limit
    for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
    {
        //Report progress
        thisWorker.ReportProgress(PrimeCandidate);
        bool isNoPrime = false;

        //Check if the Cancelation was requested during the last loop
        if (thisWorker.CancellationPending)
        {
            //Tell the Backgroundworker you are canceling and exit the for-loop
            e.Cancel = true;
            break;
        }

        //Determin if this is a Prime Number
        for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
        {
            if (PrimeCandidate % j == 0)
                isNoPrime = true;
        }

        if (!isNoPrime)
            Primes.Add(PrimeCandidate);
    }

    //Tell the progress bar you are finished
    thisWorker.ReportProgress(highestToCheck);

    //Save Return Value
    e.Result = Primes.ToArray();
}

private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    pgbPrim.Value = e.ProgressPercentage;
}

private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    pgbPrim.Value = pgbPrim.Maximum;
    this.Refresh();

    if (!e.Cancelled && e.Error == null)
    {
        //Show the Result
        int[] Primes = (int[])e.Result;

        StringBuilder sbOutput = new StringBuilder();

        foreach (int Prim in Primes)
        {
            sbOutput.Append(Prim.ToString() + Environment.NewLine);
        }

        tbPrim.Text = sbOutput.ToString();
    }
    else 
    {
        tbPrim.Text = "Operation canceled by user or Exception";
    }
}
#endregion

谢谢克里斯托弗,

我接受了你的回答,因为你给了我一些解决问题的建议。 你可以在下面看到我是如何解决这个问题的

拉德

//class variable
private SimulatorRunner simulatorRunner;

//Code behind DevicesSimulatorForm form
private void RunSimulator()
{
 btnRerun.BackColor = Color.BurlyWood;
 ParameterizedThreadStart start = new ParameterizedThreadStart(RunSimulator);
 Thread simulatorProcessingThread = new Thread(start);
 simulatorProcessingThread.Start(this);
}

//This will run in a separate thread so when accessing controls Invoke is being used.
public void RunSimulator(object form)
{
 DevicesSimulatorForm devicesSimulatorForm = (DevicesSimulatorForm) form;
 simulatorRunner.Run(devicesSimulatorForm);
        devicesSimulatorForm.InvokeEx(formInner =>
 {
  formInner.btnRerun.BackColor = Color.LightGray;
  InitializeFields();
  InitializeTextBackBorder();
  InitializeButtonControls();
  running = false;
 });

}


public class SimulatorRunner
{
   public void Run(DevicesSimulator form)
    { 
    
      string buffer = "Some content read from file in a loop that needs to be passed 
                      to a rich text box when a boolean Intercept check box is true
                      and FormStatusIntercept will return true and with Thread.Sleep(1)
                      we will have a chance to update the buffer to the new value and by
                      unchecking Intercept check box we will exit while loop and continue
                      processing"      
      while (true)
      {
        if (FormStatusIntercept(form, ref buffer))
        {
          Thread.Sleep(1);
        }
        else
        {
          publishEventArgs.Buffer = buffer;
          break;
        }
      }

      PublishEvent?.Invoke(this, publishEventArgs);
   }
}

private bool FormStatusIntercept(DevicesSimulator simulatorForm, ref string buffer)
{
 string modifiedBuffer = buffer;
  //When btnFormStatus button is pressed it changes FormStatusContinued = true
  //which allows continuation of the processing by exiting while loop
 if (simulatorForm.FormStatusContinued == true)
 {
  simulatorForm.InvokeEx(form =>
  {
   if (form.rtbFormStatus.Text != modifiedBuffer)
   {
    modifiedBuffer = form.rtbFormStatus.Text;
   }
   form.FormStatusContinued = false;
   form.FormStatusInterceptPending = false;
  });
  buffer = modifiedBuffer;
  return false;
 }
 else if (simulatorForm.FormStatusIntercept == true)
 {
  if (simulatorForm.FormStatusInterceptPending == false)
  {
      //Whith check box pressed (true) we request a stop
      //and we enter a while loop with Thread.Sleep(1)
   simulatorForm.InvokeEx(form =>
   {
    form.btnFormStatus.Text = "Continue";
    form.rtbFormStatus.Text = modifiedBuffer;
    form.FormStatusInterceptPending = true;
   });
  }
  return true;
 }
 return false;
}