Обновления пользовательского интерфейса с использованием BeginInvoke зависли до завершения потока?

#c# #multithreading #events #forms #invoke

#c# #многопоточность #Мероприятия #формы #вызвать

Вопрос:

У меня есть 1 класс (acBL), который обрабатывает 2 потока (fpDoWork). Любая работа, выполняемая в fpDoWork, вызовет событие возврата к классу acBL. В моей форме я объявляю класс acBL и связываю обработчики событий с acBL — таким образом, всякий раз, когда вызывается событие, оно «должно» обновлять пользовательский интерфейс изменениями.

Чего не происходит, так это того, что при запуске каждого потока fpDoWork он выполняет операции, вызывается ProcessingEvent и переходит в событие frmMain.handlerProcessing1. Доходит до того, что это.Вызывается BeginInvoke (new Processing2Event (handlerProcessing2), status), затем он просто зависает и ждет, пока поток не завершит свою работу, прежде чем продолжить обновление пользовательского интерфейса. Я пробовал это.Вызываю, но этот метод, похоже, просто зависает. Есть идеи?

Код внутри frmMain

Ранее в коде:

 this.acBL.Processing1Event  = new acBL.Processing1(handlerProcessing1);
this.acBL.Processing2Event  = new acBL.Processing2(handlerProcessing2);
  

Обработчики:

 private void handlerProcessing1(string status) {
  if (InvokeRequired) {
    this.BeginInvoke(new MethodInvoker(this.Refresh));
    this.BeginInvoke(new Processing1Event (handlerProcessing1), status);

  }
  else {
    Console.WriteLine("UPDATING 1: " status);
    lblForProcessing1.Text = status;
    this.Refresh();
    return;
  }
}


private void handlerProcessing2(string status) {
  if (InvokeRequired) {
    this.BeginInvoke(new MethodInvoker(this.Refresh));
    this.BeginInvoke(new Processing2Event (handlerProcessing2), status);

  }
  else {
    Console.WriteLine("UPDATING 2: " status);
    lblForProcessing2.Text = status;
    this.Refresh();
    return;
  }
}
  

Код внутри acBL

В основном методе:

 bool thread1Complete = false;
fpDoWork fpDoWork1 = new fpDoWork();
fpDoWork1.GUIForm = frmMain;
fpDoWork1.ProcessingEvent  = new fpDoWork.Processing(handlerProcessing1Event);
fpDoWork1.ThreadCompleteEvent  = new fpDoWork.ThreadComplete(thread1Complete);
Thread fpDoWork1Thread= new Thread(new ThreadStart(fpDoWork1.StartWork));

bool thread2Complete = false;
fpDoWork fpDoWork2 = new fpDoWork();
fpDoWork2.GUIForm = frmMain;
fpDoWork2.ProcessingEvent  = new fpDoWork.Processing(handlerProcessing2Event);
fpDoWork2.ThreadCompleteEvent  = new fpDoWork.ThreadComplete(thread2Complete);
Thread fpDoWork2Thread= new Thread(new ThreadStart(fpDoWork2.StartWork));

Console.WriteLine("Work for 1 Thread started...");
fpDoWork1Thread.Start();
Console.WriteLine("Work for 2 Thread started...");
fpDoWork2Thread.Start();

while (!thread1Complete amp;amp; !thread2Complete ) {
  if (!thread1Complete amp;amp; !thread2Complete ) {
    Console.WriteLine("Waiting for both copying threads...");
  }
  else if (!thread1Complete amp;amp; thread2Complete ) {
    Console.WriteLine("Waiting for thread 1...");
  }
  else if (thread1Complete amp;amp; !thread2Complete ) {
    Console.WriteLine("Waiting for thread 2...");
  }
  Thread.Sleep(1000);
}
Console.WriteLine("All done");
  

Еще где в коде:

 public delegate void ProcessingFor1 (string filename);
public delegate void ProcessingFor2 (string filename);
public event ProcessingFor1 ProcessingEventFor1;
public event ProcessingFor2 ProcessingEventFor2;


private void handlerProcessing1Event(string filename) {
  Console.WriteLine("Processing 1: "   filename);
  ProcessingEventFor1(filename);
}

private void handlerProcessing1Event(string filename) {
  Console.WriteLine("Processing 2: "   filename);
  ProcessingEventFor2(filename);
}
  

Комментарии:

1. Вы создаете взаимоблокировку, блокируя поток пользовательского интерфейса, зацикливаясь на флагах. Цель BeginInvoke не может быть запущена до тех пор, пока поток пользовательского интерфейса не перейдет в режим ожидания, возвращаясь к циклу передачи сообщений. Вы никогда не должны блокировать поток пользовательского интерфейса. Избегайте повторного изобретения проблемы, используйте BackgroundWorker. И не зацикливайтесь на его свойстве IsBusy, та же проблема. Делайте все, что хотите, после завершения потока в вашем обработчике событий RunWorkerCompleted.

2. У меня нет времени перекодировать, чтобы включить BackgroundWorker. Я пытался реализовать это раньше, но обнаружил ограничения. Есть ли способ исправить взаимоблокировку в этом сценарии?

3. Комментарий Ханса верен. Исправление взаимоблокировок требует времени. Я поддерживаю его предложение, чтобы вы потратили время на правильное исправление. Любое взломанное решение (такое как цикл вложенных сообщений) приведет только к появлению странных ошибок, с которыми вам придется иметь дело в будущем; это сэкономит вам много времени, если исправить это раньше, а не позже.

4. @Nobugz: Я видел, как вы отвечали на многие вопросы комментариями вместо ответов. Есть какая-то конкретная причина?

Ответ №1:

причина, по которой ваш пользовательский интерфейс не обновляется, заключается в том, что основной поток пользовательского интерфейса застрял в вашем цикле while(). BackgroundWorker твой друг

старайтесь избегать создания потоков в одном потоке и сидеть там, ожидая их завершения, в первую очередь, это плохое использование потоков

приветствия