Windows Forms & Invoke from parallel threads
When remaking an old form, I ran into a funny problem.
The task is classical: display information to the user about what is happening in the background process.
It would seem nothing complicated. In the main form, we start the stream, we process the data in it, when receiving new statuses - we drop the update on the form, synchronizing with the base UI Thread (Invoke / BeginInvoke call).
And all is well until the moment when our background thread tries to create one or two more ... by which it delegates additional work as part of the task. It is with these new flows that the leapfrog begins ...
So, the first thread received the text for updating, we will display this text on the form.

or
In one thread, this works - messages arrive, the form receives them, data is updated. When adding a few more, our SendNotificationToForm function starts to behave not quite as expected. Messages arrive and the form will receive them, it just puts them in the queue for updating the content and until your background threads finish working, he is in no hurry to display them. And the more complex the task you are doing in other flows, the more clearly this is manifested. Moreover, in real code, you will probably need to not only change one Label, it is quite possible that the data set for changing the visualization will be much more complicated.

The official documentation on this subject stubbornly talks about InfokeRequired & BeginInvoke. But in fact, in our case, we will have to abandon the “usual method” and go to the manual control of the synchronization context. Since we need updates to occur at the moment the data arrives on the form, we will have to carry out “manual synchronization”.
To do this, after creating the form (when the context has already been created and initialized), remember it:
And now, in our callback from another thread, we execute the command:
In this case, we forced the data to be synchronized for the form and forced the main UI Thread to process the received information.
From the point of view of the problem, the solution is the simplest, but effective. But it gives a guarantee that now the user will see on the form all the information from the working threads, and not just a part of the data from the first.
The task is classical: display information to the user about what is happening in the background process.
It would seem nothing complicated. In the main form, we start the stream, we process the data in it, when receiving new statuses - we drop the update on the form, synchronizing with the base UI Thread (Invoke / BeginInvoke call).
And all is well until the moment when our background thread tries to create one or two more ... by which it delegates additional work as part of the task. It is with these new flows that the leapfrog begins ...
So, the first thread received the text for updating, we will display this text on the form.

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;
}
or
private void SendNotificationToForm( string actionText )
{
this.BeginInvoke((Action)(() =>
{
this.LabelInfo.Text = actionText;
}));
}
In one thread, this works - messages arrive, the form receives them, data is updated. When adding a few more, our SendNotificationToForm function starts to behave not quite as expected. Messages arrive and the form will receive them, it just puts them in the queue for updating the content and until your background threads finish working, he is in no hurry to display them. And the more complex the task you are doing in other flows, the more clearly this is manifested. Moreover, in real code, you will probably need to not only change one Label, it is quite possible that the data set for changing the visualization will be much more complicated.

The official documentation on this subject stubbornly talks about InfokeRequired & BeginInvoke. But in fact, in our case, we will have to abandon the “usual method” and go to the manual control of the synchronization context. Since we need updates to occur at the moment the data arrives on the form, we will have to carry out “manual synchronization”.
To do this, after creating the form (when the context has already been created and initialized), remember it:
Fields:
private readonly SynchronizationContext syncContext;
Constructor:
syncContext = SynchronizationContext.Current;
And now, in our callback from another thread, we execute the command:
private void SendNotificationToForm( string actionText )
{
syncContext.Post( UpdateUI, actionText);
}
private void UpdateUI(actionText)
{
this.LabelInfo.Text = actionText;
}
In this case, we forced the data to be synchronized for the form and forced the main UI Thread to process the received information.
From the point of view of the problem, the solution is the simplest, but effective. But it gives a guarantee that now the user will see on the form all the information from the working threads, and not just a part of the data from the first.