I was playing with Task.ConfigureAwait
in order to better understand what is going beyond the hood. So i got this strange behavior while combining some UI access stuff with the ConfigureAwait
.
Below is the sample app using a simple windows form, with 1 Button
followed by the test results:
private async void btnDoWork_Click(object sender, EventArgs e)
{
List<int> Results = await SomeLongRunningMethodAsync().ConfigureAwait(false);
int retry = 0;
while(retry < RETRY_COUNT)
{
try
{
// commented on test #1 & #3 and not in test #2
//if(retry == 0)
//throw new InvalidOperationException("Manually thrown Exception");
btnDoWork.Text = "Async Work Done";
Logger.Log("Control Text Changed", logDestination);
return;
}
catch(InvalidOperationException ex)
{
Logger.Log(ex.Message, logDestination);
}
retry++;
}
}
Now after button Click:
Test 1 Log results : (Exactly as the above code)
1. Cross-thread operation not valid: Control 'btnDoWork' accessed from a thread other than the thread it was created on.
2. Control Text Changed
Test 2 Log results : (Manual exception throw uncommented)
1. Manually thrown Exception
2. Cross-thread operation not valid: Control 'btnDoWork' accessed from a thread other than the thread it was created on.
3. Control Text Changed
Test 3 Log results : (Same as 1 but without a debugger)
1. Control Text Changed
So the questions are:
Why does the first UI Access (Cross-Thread Operation) have the next iteration of the loop execute on the Main Thread ?
Why doesn't the manual exception lead to the same behavior ?
Why does executing the above sample without a debugger attached (directly from exe) doesn't show the same behavior ?
This one got me scratching my head a bit, but finally found the trick.
The code of the setter of the
Button.Text
property is:The line throwing the exception is
this.WindowText = value;
(because it internally tries to access theHandle
property of the button). The trick is that, right before, it sets thetext
property in some kind of cache:I'll be honest, I have no clue how this cache works, or when it is activated or not (turns out, it seems to be activated in this precise case). But because of this, the text is set even though the exception was thrown.
On further iterations of the loop, nothing happens because the property has a special check to make sure you don't set the same text twice:
If you change your loop to set a different text every time, then you'll see that the exception is thrown consistently at each iteration.