I have a winform code which run after a button click :
void button1_Click(object sender, EventArgs e)
{
AAA();
}
async Task BBB( int delay)
{
await
Please note that nested message loops are evil because unexpected reentrancy is Just Too Darn Hard(tm).
I think there are two key pieces of understanding to explain this behavior. The first is that async continuations - like all other "run this arbitrary code" Win32 messages - have a higher priority than other messages. The second is that there's a long-standing Win32 tradition of sending messages and synchronously blocking for a response while running a nested message loop. (On a side note, it is my personal opinion that this horrible reentrancy-everywhere design of the Win32 API has been responsible for the vast majority of application bugs on Windows).
If you run your code in a way that preserves stack traces, you can see more clearly what's going on:
void button1_Click(object sender, EventArgs e)
{
AAA();
}
private List<string> stacks = new List<string>();
async Task BBB(int delay)
{
await Task.Delay(TimeSpan.FromSeconds(delay));
var stack = new StackTrace().ToString();
stacks.Add(stack);
MessageBox.Show(stack);
}
async Task AAA()
{
var task1 = BBB(1); // <--- notice delay=1;
var task2 = BBB(1); // <--- notice delay=1;
var task3 = BBB(1); // <--- notice delay=1;
await Task.WhenAll(task1, task2, task3);
Clipboard.SetText(string.Join("\r\n\r\n", stacks));
}
Compare the dialog texts (largest stack first, then medium, then smallest) with the clipboard after the dialogs are all closed (smallest first, then medium, then largest). It's clear that the dialogs are being displayed in the reverse order.
I believe something like this is happening, but lack the confidence to say for sure:
MessageBox.Show
.MessageBox
function starts a nested message loop and starts setting up the actual dialog with messages to itself (i.e., setting caption, text, etc). Note that these calls pump messages but they're not ready to show the dialog yet.MessageBox.Show
.When you change the timings to 1, 2, 3
, you'll still get the same stacks in the clipboard, but you'll see the dialog texts are now in order (smallest stack first, then medium, then largest). This is because each MessageBox.Show
has sufficient time to set up the message box and establish its message loop and show the dialog before the next one layers on top of it.
In theory, this odd behavior could be avoided by a MessageBox.ShowAsync
API that avoids the nested loops completely. I wouldn't hold my breath for it, though.