What happens when async code attempts to resume on a thread that is already executing?

前端 未结 2 462
挽巷
挽巷 2021-01-26 20:55

I feel that the answer to this is due to me having an incorrect concept of how threads work, but here goes.

private void button1_Click(object sender, EventArgs e         


        
2条回答
  •  庸人自扰
    2021-01-26 21:37

    This is the secret that you do not understand: I give you the Windows Message Loop

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        MSG msg;
        BOOL bRet;
        while(TRUE)
        {
            bRet = GetMessage(&msg, NULL, 0, 0);
            if (bRet <= 0) break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    

    This is the actual "main" of your application; you just don't see it because it is hidden behind the scenes.

    A simpler loop could not be imagined. It gets a message from the queue. If there are no more messages then the program must be done. If there was a message then it runs the standard message translations and dispatches the message, and then keeps on running.

    How is it possible to resume on a thread that is already running other code?

    It isn't. "Resuming on a thread that is running other code" is actually putting a message in the queue. That "other code" is being synchronously called by DispatchMessage. When it is done, it returns to the loop, the queue is polled, and the message indicates what code needs to be dispatched next. That then runs synchronously until it returns back to the loop.

    What forces the thread to switch between the two sections of code that want to run?

    Nothing. That doesn't happen.

    In general, what happens when you attempt to resume on a thread that is already running some other code?

    The message that describes what continuation needs to be run is queued up.

    I suppose this isn't any different to how my click event runs on the UI thread in the first place, in as much as I know it runs on the UI thread, and I know the UI thread is also doing other stuff, but I've not really thought about it like this before.

    Start thinking about it.

    Click events are exactly the same. Your program is doing something; you click the mouse; the click hander does not interrupt the UI thread and start running new work on it. Rather, the message is queued up, and when your UI thread control returns to the message loop, the click is eventually processed; DispatchMessage causes Button1_OnClick to be invoked via some mechanism in Windows Forms. That's what WinForms is; a mechanism for translating Windows messages into calls to C# methods.

    But you already knew that. You know that when an event-driven program does a long-running synchronous operation, that the UI freezes, but that click events are processed eventually. How did you think that happened? You must have understood at some level that they were being queued up for processing later, right?

    Exercise: What does DoEvents do?

    Exercise: Given what you now know: what could possibly go wrong if you call DoEvents in a loop to unblock your UI?

    Exercise: How is await different from DoEvents in a GUI application?

提交回复
热议问题