При переделке старой формы столкнулся с забавной проблемой.
Задача – классическая: вывести пользователю информацию о происходящем в фоне процессе.
Казалось бы, ничего сложного. В основной форме мы стартуем поток, в нем проводим обработку данных, при получении новых статусов – сбрасываем обновление на форму, проводя синхронизацию с базовым UI Thread (Invoke / BeginInvoke call).
И все хорошо до момента, пока наш background поток не попытается создать еще один-два-… Которым делегирует дополнительную работу в рамках задачи. Вот с этими-то новыми потоками и начинается чехарда…
Итак, первый поток получил текст для обновления, этот текст будем выводить на форму.

или
В рамках одного потока это работает – сообщения приходят, форма их получает, данные обновляются. При добавлении же еще нескольких, наша функция SendNotificationToForm начинает вести не совсем так, как ожидалось. Сообщения приходят и форма их получат, вот только складывает их в очередь для обновления контента и пока ваши background потоки не закончат работу, не спешит выводить на экран. И чем более сложной задачей заняты вы в других потоках, тем нагляднее это проявляется. Причем в реальном коде вам наверняка надо будет не только один Label изменить, вполне в��зможно, что набор данных для смены визуализации будет куда как более сложным.

Официальная документация на эту тему упорно говорит про InfokeRequired & BeginInvoke. Но на самом деле, в нашем случае придется отказаться от «обычного метода» и перейти к ручному управлению контекстом синхронизации. Так как нам нужно, чтобы обновления произошли в момент прихода данных на форму, то и придется провести «ручную синхронизацию».
Для этого – после создания формы (когда контекст уже создан и проинициализирован) – запомнить его:
И теперь уже в нашем callback из другого потока выполним команду:
В этом случае – мы принудительно провели синхронизацию данных для формы и заставили основной UI Thread обработать полученную информацию.
С точки зрения проблемы – решение простейшее, но эффективное. Но оно дает гарантию, что теперь пользователь увидит на форме всю информацию от работающих потоков, а не только часть данных от первого.
Задача – классическая: вывести пользователю информацию о происходящем в фоне процессе.
Казалось бы, ничего сложного. В основной форме мы стартуем поток, в нем проводим обработку данных, при получении новых статусов – сбрасываем обновление на форму, проводя синхронизацию с базовым UI Thread (Invoke / BeginInvoke call).
И все хорошо до момента, пока наш background поток не попытается создать еще один-два-… Которым делегирует дополнительную работу в рамках задачи. Вот с этими-то новыми потоками и начинается чехарда…
Итак, первый поток получил текст для обновления, этот текст будем выводить на форму.

private delegate void EditStatusTextDelegate(string strArg);
private void SendNotificationToForm( string actionText )
{
if (this.InvokeRequired)
{
this.Invoke(new EditStatusTextDelegate(UpdateUI), new object[] { actionText });
return;
}
UpdateUI(actionText);
}
private void UpdateUI(actionText)
{
this.LabelInfo.Text = actionText;
}
или
private void SendNotificationToForm( string actionText )
{
this.BeginInvoke((Action)(() =>
{
this.LabelInfo.Text = actionText;
}));
}
В рамках одного потока это работает – сообщения приходят, форма их получает, данные обновляются. При добавлении же еще нескольких, наша функция SendNotificationToForm начинает вести не совсем так, как ожидалось. Сообщения приходят и форма их получат, вот только складывает их в очередь для обновления контента и пока ваши background потоки не закончат работу, не спешит выводить на экран. И чем более сложной задачей заняты вы в других потоках, тем нагляднее это проявляется. Причем в реальном коде вам наверняка надо будет не только один Label изменить, вполне в��зможно, что набор данных для смены визуализации будет куда как более сложным.

Официальная документация на эту тему упорно говорит про InfokeRequired & BeginInvoke. Но на самом деле, в нашем случае придется отказаться от «обычного метода» и перейти к ручному управлению контекстом синхронизации. Так как нам нужно, чтобы обновления произошли в момент прихода данных на форму, то и придется провести «ручную синхронизацию».
Для этого – после создания формы (когда контекст уже создан и проинициализирован) – запомнить его:
Fields:
private readonly SynchronizationContext syncContext;
Constructor:
syncContext = SynchronizationContext.Current;
И теперь уже в нашем callback из другого потока выполним команду:
private void SendNotificationToForm( string actionText )
{
syncContext.Post( UpdateUI, actionText);
}
private void UpdateUI(actionText)
{
this.LabelInfo.Text = actionText;
}
В этом случае – мы принудительно провели синхронизацию данных для формы и заставили основной UI Thread обработать полученную информацию.
С точки зрения проблемы – решение простейшее, но эффективное. Но оно дает гарантию, что теперь пользователь увидит на форме всю информацию от работающих потоков, а не только часть данных от первого.