How to ensure that async method finished work?

后端 未结 2 1947
时光说笑
时光说笑 2020-12-20 07:25

I\'m very new to threads, so my thoughts and questions might be a bit silly :)

I fill WinForm control with data from another thread, so I have to call <

相关标签:
2条回答
  • 2020-12-20 07:49

    This is absolutely wrong:

    treeViewWriter.Start();
    treeViewWriter.Join();
    

    Never call Thread.Join from Main Thread! because that Join freezes the application and all those BeginInvoke/Invoke never gets fully executed because the message is not handled.

    This is how BeginInvoke() actually works:

    1. It sends some WM_USER (or the like) on message loop.
    2. Main thread pops this message in Application.DoEvents() (or the like which is always called in Application.Run())
    3. Main thread executes the delegate passed to BeginInvoke()
    4. Main thread signals end of execution (by WaitHandle in IAsyncResult)
    5. EndInvoke() waits for such signal (or if IAsyncResult from BeginInvoke is never stored, it gets garbage-collected)

    So again: you eiter write it purely event-driven or do something like this:

    private bool done = false;
    void click(object, EventArgs) {
        thread.Start();
        while(!done) Application.DoEvents();
        tree.ExpandAll();
    }
    

    ADDON: Eihter use Invoke() (synchronized) and the above loop with Application.DoEvents()
    or use BeginInvoke() and call your ExpandAll the same way (through BeginInvoke() from the thread)

    ADDON2:

    private bool done;
    void click(object,EventArgs) {
        done = false; // init state
        new Thread(work).Start(); // start backgound work
        while(!done) Application.DoEvents(); // wait until done
        finish(); } // finish the job in main thread
    void work() {
        Thread.Sleep(100); // do your work
        done = true; } // signal done
    void finish() {
        whatever(); } // called on main thread
    
    void click2(object,EventArgs) {
        new Thread(work2).Start(); } // just start the hread
    void work2() {
        Thread.Sleep(100); // do your work
        BeginInvoke(new Action(finish)); } // execute finish() on main thread
    
    0 讨论(0)
  • 2020-12-20 08:13

    Create an Action which Invokes the delegate, then BeginInvoke that action. This way you will have a callback which you can move the ExpandAll into:

    if (tree.InvokeRequired)
            new Action(() => { tree.Invoke(code, tree); }).BeginInvoke((ar) => {
                treeDirectoryContents.ExpandAll();
            }, null);
    else
        code.Invoke(tree);
    

    Note that I replaced your original BeginInvoke with a simple Invoke.

    UPDATE: As firda mentioned correctly, because the main thread is blocked inside the Join method waiting for the other thread to exit, executing Invoke on the controls will lead to a deadlock. So now that your ExpandAll is moved to the callback, you should remove the Join and everything will be fine.

    0 讨论(0)
提交回复
热议问题