understanding InvalidAsynchronousStateException occurrences

后端 未结 3 1592
广开言路
广开言路 2020-12-30 00:32

When does InvalidAsynchronousStateException get thrown?

I have the following piece of code:

control.InvokeRequired ? control.Invoke(expressi

3条回答
  •  不思量自难忘°
    2020-12-30 00:55

    As others have correctly shown this happens when a UI component is disposed or is completed (return) while a different thread is still invoking code on the same UI component.

    This usually happens when a user closes a window (a form) that has been updated by a different thread and this is more prevalent when the update frequency is high (because the chance of having an incomplete invoke when the user closes the form is high).

    This can be gracefully handled by:

    1. Set a flag to indicate an invoke is in progress
    2. Intercept the UI dispose or return
    3. Stop further (new) invokes from taking place
    4. Wait until existing invokes finish
    5. Complete the intended dispose or return

    Below example shows how to gracefully handle the most common scenario (when a form is closed while it's been updated).

    The example is from a simple form that has a listbox that is updated from an outside thread via a public method (AddItem(string)).

    Flags

    private bool invokeInProgress = false;
    private bool stopInvoking = false
    

    Invoking code

    public void AddItem(string newItem)
    {
    
        if (listView1.InvokeRequired)
        {
            if (stopInvoking != true) // don't start new invokes if the flag is set
            {
                invokeInProgress = true;  // let the form know if an invoke has started
    
                listView1.Invoke(new Action(() => addItem(newItem)));  // invoke
    
                invokeInProgress = false;  // the invoke is complete
            }
    
            return;
        }
    
        listView1.Items.Add(newItem);
        listView1.Items[listView1.Items.Count - 1].EnsureVisible();
    }
    

    Intercepting and managing form closing event

    private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (invokeInProgress)
        {
            e.Cancel = true;  // cancel the original event 
    
            stopInvoking = true; // advise to stop taking new work
    
            // now wait until current invoke finishes
            await Task.Factory.StartNew(() =>
                            {
                                while (invokeInProgress);  
                            });
    
            // now close the form
            this.Close();
        }
    }
    

    You can further extend this by exposing a method or a property that lets the users (other threads) know that the form is shutting down so that the callers can gracefully handle the situation.

    Example below shows how a new property (ShuttingDown) allows a caller to handle its flow correctly if a user closes the display form.

    New flag on form

    public bool ShuttingDown { get { return stopInvoking; } }
    

    Caller now can detect the problem

    static void Main()
    {
        Form1 frm = new Form1();
    
        Task.Factory.StartNew(() => frm.ShowDialog(), TaskCreationOptions.LongRunning);
    
        int i = 0;
        while (i < 2000)
        {
            if (frm.ShuttingDown != true)  // the clients can also be notified and allowed to handle the UI disruption
            {
                frm.addItem(Guid.NewGuid().ToString());
            }
            else
            {
                MessageBox.Show("Form is closing. Stopping the process.");
                break;
            }
    
            i++;
        }
    
        MessageBox.Show("Program completed! i=" + i.ToString());
    }
    

    You can read more and download the sample project from here: http://www.ilearnttoday.com/c-sharp-the-destination-thread-no-longer-exists

提交回复
热议问题