Note that there are some explanatory texts on larger screens.

plurals
  1. POWinForms Multithreading: Execute a GUI update only if the previous one has finished
    text
    copied!<p>I have multithreaded application with some background processing. It has <strong>long-running UI updates</strong> (on the UI-thread itself), which are invoked from the background thread via the well-known <a href="http://msdn.microsoft.com/en-us/library/757y83z4%28VS.71%29.aspx" rel="nofollow noreferrer">resource on MSDN</a>. I can not shorten these UI updates since they are finally done in a external library(1). </p> <p>Now, from that background thread, <strong>I want to asynchronously invoke (using <code>BeginInvoke()</code>) these updates on the UI thread, but only if the previous update has finished yet.</strong> If not, I would like to simply skip this update. This will prevent an overflow of the Windows Message Queue, in case the invokes come faster than the to invoked method is able to execute.</p> <p><strong>My current solution is this:</strong> In the method that executes on the UI thread, I do enter and exit a ReaderWriterLockSlim instance. On the background thread, I try to enter the instance with zero timeout. When successful, I call 'BeginInvoke()' then exit again. When not successful, I skip the method invoke altogether.</p> <pre><code>public void Update(double x, double y) { _updateLock.EnterWriteLock(); try { //...long running task... } finally { _updateLock.ExitWriteLock(); } } //.... void Provider_PositionChanged(object sender, SpecialEventArgs e) { if (_updateLock.TryEnterWriteLock(0)) //noone is currently executing an update? { try { myUiControl.BeginInvoke(/*...*/); } finally { _updateLock.ExitWriteLock(); } } </code></pre> <p><strong>This all works, but is there a more elegant solution? How to simply test, from the one thread, whether a method is executed on any (other) thread?</strong></p> <ul> <li><em>Note: Using <code>Invoke()</code> (instead of <code>BeginInvoke()</code>) is not an option, since this would block my background thread, preventing other stuff from executing there.</em></li> <li>(1)<em>It's MapXtreme, a mapping solution, and I want to pan/zoom large bitmap terrain data, plus updating some features.</em></li> <li><em>PS. This question is somewhat related, but covers a different aspect: <a href="https://stackoverflow.com/questions/4283689/winforms-multithreading-is-creating-a-new-delegate-each-time-when-invoking-a-met">Winforms multithreading: Is creating a new delegate each time when invoking a method on the UI thread needed?</a></em></li> </ul> <p>Thanks for any answers!</p> <p><strong>UPDATE:</strong> Hans Passant has helped me with his answer. <strong>See the solution below.</strong> Hopefully this helps someone else too.</p> <pre><code>/// &lt;summary&gt; /// This class enqueues asynchronously executing actions (that are running on another thread), but allows /// to execute only one action at a time. When busy, newly enqueued actions are dropped. /// Any enqueued action is required to call Done() on this when it has finished, to allow further actions /// to execute afterwards. /// &lt;/summary&gt; /// &lt;remarks&gt;This class is intended to help prevent stacking UI-Updates when the CPU or other resources /// on the machine are not able to handle the amount of updates requested. However, the user /// must keep in mind, that using this class may result /// in dropped updates and that the last requested update is not always executed.&lt;/remarks&gt; public class ActionBouncer { /// &lt;summary&gt; /// A event that signals the idle/busy state. Idle means, that no action is currently executing. /// &lt;/summary&gt; private ManualResetEvent _idle = new ManualResetEvent(true); /// &lt;summary&gt; /// Enqueues the specified action, executing it when this bouncer /// is currently idle. /// &lt;/summary&gt; /// &lt;param name="action"&gt;The action.&lt;/param&gt; public void Enqueue(Action action) { if (_idle.WaitOne(0)) //are we idle now? (Remark: This check and the reset below is not thread-safe (thanks to s.skov)) { _idle.Reset(); //go to busy state action(); //execute the action now. }//else drop the action immediately. } /// &lt;summary&gt; /// Signal the bouncer, that the currently executing asynchronous action is done, allowing /// subsequent requests to execute. /// This must get explicitly called (in code) at the end of the asynchronous action. /// &lt;/summary&gt; public void Done() { _idle.Set(); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload