Note that there are some explanatory texts on larger screens.

plurals
  1. POEnsuring execution order between multiple threads
    text
    copied!<p>I am having a kinda annoying problem mostly due to my low skill level/experience in C# multithreading.</p> <p>Here is the background. In my framework, I have a static class named <code>WaitFormHelper</code>, which has two static methods (well... actually more but we don't care here), <code>Start()</code> and <code>Close()</code> The <code>Start()</code> method initializes and starts a thread which will acquire a lock on the <code>locker</code> object and create a <code>WaitForm</code> (which is a small loading control with a custom message and a progress bar)</p> <p>In my current project, I have a method which starts a <code>WaitForm</code>, performs calculations, then closes the <code>WaitForm</code>. Nothing fancy at all. The method looks like the following, I simplified it as much as possible:</p> <pre><code>public void PerformCalculations() { try { WaitFormHelper.Start("Title", "message", false); if (this.CalculationsParameters.IsInvalid) { return; } // Perform all those lengthy calculations here } // catch whatever exception I may have to catch, we don't care here finally { WaitFormHelper.Close(); } } </code></pre> <p>Here are the <code>Start()</code> and <code>Close()</code> methods with related methods &amp; attributes, simplified as well:</p> <pre><code>private static Thread instanceCaller; private static WaitForm instance; private static AutoResetEvent waitFormStarted = new AutoResetEvent(false); private static object locker = new object(); /// &lt;summary&gt; /// Initializes WaitForm to start a single task /// &lt;/summary&gt; /// &lt;param name="header"&gt;WaitForm header&lt;/param&gt; /// &lt;param name="message"&gt;Message displayed&lt;/param&gt; /// &lt;param name="showProgressBar"&gt;True if we want a progress bar, else false&lt;/param&gt; public static void Start(string header, string message, bool showProgressBar) { InitializeCallerThread(showProgressBar, header, message); instanceCaller.Start(); } /// &lt;summary&gt; /// Initializes caller thread for executing a single command /// &lt;/summary&gt; /// &lt;param name="showProgressBar"&gt;&lt;/param&gt; /// &lt;param name="header"&gt;&lt;/param&gt; /// &lt;param name="message"&gt;&lt;/param&gt; private static void InitializeCallerThread(bool showProgressBar, string header, string message) { waitFormStarted.Reset(); instanceCaller = new Thread(() =&gt; { lock (locker) { instance = new WaitForm() { Header = header, Message = message, IsProgressBarVisible = showProgressBar }; waitFormStarted.Set(); } instance.ShowDialog(); }); instanceCaller.Name = "WaitForm thread"; instanceCaller.SetApartmentState(ApartmentState.STA); instanceCaller.IsBackground = true; } /// &lt;summary&gt; /// Closes current form /// &lt;/summary&gt; public static void Close() { lock (locker) { if (instance != null &amp;&amp; !instance.IsClosed) { waitFormStarted.WaitOne(); instance.FinalizeWork(); instance.Dispatcher.Invoke( new Action(() =&gt; { instance.Close(); })); } } } </code></pre> <p><strong>Now let's get to the problem</strong></p> <p>This usually works fine, except in this case: If <code>this.CalculationsParameters.IsInvalid</code> is true (ie. as you probably already understood, I can't perform calculations for some reason, such as "user entering crap in my forms"), the execution directly closes my <code>WaitForm</code>. However in this case, the main thread will reach the <code>Close</code> method and acquire a lock on the <code>locker</code> object <strong>before</strong> the thread fired by the <code>Start()</code> method.</p> <p>What happens is that: <code>Close</code> acquires lock, tries to close the form but <code>instance</code> is still null because the <code>Thread</code> created in <code>InitializeCallerThread</code> is still waiting for the lock to actually create it. <code>Close</code> releases lock, <code>InitializeCallerThread</code> acquires it and... shows a <code>WaitForm</code> which will not close.</p> <p>Now I know that I can simply fix this problem by testing if the calculation parameters are invalid before actually starting the WaitForm, but well, problem is this <code>WaitForm</code> is supposed to be used by all applications of our framework (which includes 40+ different apps used and maintained in 4 countries), so ideally I'd rather prefer seeing my <code>WaitForm</code> working at all cases.</p> <p>Do you have any idea on how could I synchronize this to make sure the starter thread is definitely called and executed first? As you can see I already use an <code>AutoResetEvent</code> for this matter, but if I listen to it before the test <code>if (instance != null)</code> I will end up stuck in my case.</p> <p>Hope this is clear enough! Thank you!</p>
 

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