本文介绍了异步等待的延续连发 - 不同的表现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个winform code这一个按钮,点击后运行:

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 Task.Delay(TimeSpan.FromSeconds(delay));
    MessageBox.Show("hello");
}

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);
}

问:

为什么我去看One的MessageBox而此时延迟= 1

Why do I see one MessageBox at a time when delay=1 :

但是,如果我改变延迟: 1,2,3 -

But If I change delay to : 1,2,3

    var task1 = BBB(1);
    var task2 = BBB(2);
    var task3 = BBB(3);

我见 - 3消息框,甚至没有任何点击消息框

I see - 3 Message Boxes without even clicking any Messagebox?


  • 感谢@Noseratio的pointing这种行为在首位。

  • Thanks to @Noseratio for pointing that behaviour at first place.

推荐答案

请注意,嵌套邮件循环是邪恶的,因为意外重入是太差劲硬盘(TM)。

Please note that nested message loops are evil because unexpected reentrancy is Just Too Darn Hard(tm).

我想有认识的两个关键部分来解释这种行为。第一个是异步延续 - 就像所有其他的运行这个任意codeWin32的消息 - 有一个的更高的优先级的比其他消息。第二个是,有发送消息的一个长期的Win32传统的同步的阻塞响应的在运行一个嵌套的消息循环的。 (在一个侧面说明,这是我个人认为,在Win32 API的这种可怕的重入,随处可见的设计一直负责Windows上的绝大多数应用程序的bug)。

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).

如果您在preserves堆栈跟踪的方式运行code,可以更清楚地看到发生了什么:

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

  • Win32的的MessageBox 函数启动一个嵌套的消息循环,并开始建立与信息的自身实际的对话框的(即,设置标题,文本等)。请注意,这些调用抽取消息,但他们还没有准备好,以显示该对话框呢。

  • 第二个触发延时关闭,并在其自己的呼叫建立的那些消息的前跳转到 MessageBox.Show

  • 类似地,对于第三延迟。第三延迟的消息框,实际完成其设置和获取所示。另外两个消息框仍在(同步),等待他们的消息循环来的返回的数值,但由于这些循环运行code,他们无法返回。

  • The first delay fires off and calls MessageBox.Show.
  • The Win32 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.
  • The second delay fires off and jumps in front of those setup messages with its own call to MessageBox.Show.
  • Similarly for the third delay. The third delay's message box actually completes its setup and gets shown. The other two message boxes are still (synchronously) waiting for their message loops to return a value, but because those loops are running code, they can't return.

当你改变定时而 1,2,3 ,你仍然会得到剪贴板中的同一个书库,但你现在看到的对话框文本是为了(最小的堆栈,然后再中,那么最大的)。这是因为每个 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.

在理论上,这种奇怪的行为,可以通过 MessageBox.ShowAsync 的API,完全避免了嵌套循环避免。我不认为我的呼吸它,虽然。

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.

这篇关于异步等待的延续连发 - 不同的表现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 00:25