Search code examples
c#.netmultithreadingwinformsinvoke

such a thing as Control.Invoke(timeout) in WinForms?


I have some gauges in my application. Sometimes, when the UI is busy, my gauge thread stalls waiting to update some gauges. In that situation, I would like to simply abandon my plan and try updating the gauges on the next poll. I currently use the Control.Invoke to move from my data polling thread into the UI. I don't want to use BeginInvoke because I don't want to waste precious UI time updating gauges when it's only the final value that matters. Is there some other way to invoke code on the UI thread in a way that I can bail out early if I can't get on the UI thread in 40ms? Is the code in the Invoke method necessary, or is there some other way to invoke a method on the main thread?


Solution

  • There is no timeout option available. One option would be to use BeginInvoke, but only if the previous message has been processed. This will require thread synchronization, but could be written similarly to:

    // using
    object syncObj = new object();
    bool operationPending = false;
    
    while (operation)
    {
       // ... Do work
    
       // Update, but only if there isn't a pending update
       lock(syncObj)
       {
           if (!operationPending)
           {
               operationPending = true;
               control.BeginInvoke(new Action( () =>
               {
                   // Update gauges
    
                   lock(syncObj)
                      operationPending = false;
               }));
           }
       }
    }
    
    // Update at the end (since you're last update might have been skipped)
    control.Invoke(new Action(UpdateGuagesCompleted));
    

    While this wouldn't timeout, it would prevent you from "flooding" UI events onto the main thread, as only one operation would be processed at a time.


    Edit: As Yaur mentioned, this approach can also be done without locking via Interlocked:

    while (operation)
    {
        int pendingOperations = 0;
        // ... Do work
    
        // Update, but only if there isn't a pending update
        if (0 == Interlocked.CompareExchange(ref pendingOperations, 1, 0))
        {
            control.BeginInvoke(new Action( () =>
            {   
                // Update gauges
    
                // Restore, so the next UI update can occur
                Interlocked.Decrement(ref pendingOperations);
            }));
        }       
    }
    
    // Update at the end (since you're last update might have been skipped)
    control.Invoke(new Action(UpdateGuagesCompleted));