Partager via


Is this code threadsafe?

Here is a question for you multithreaded coders out there.

After executing Thread1 and Thread2, what are the possible values for "i"?

        bool b1 = false;
        bool b2 = false;
        int i = 0;

        private void Thread1() {
            b1 = true;
            if (b2) {
                i = 1;
            }
        }

        private void Thread2() {
            b2 = true;
            if (b1) {
                i = 1;
            }
        }

The answer may surprise you.  In fact, it may be zero on multiprocessor machines do to load/store optimizations made by the CPU.  Try it for yourself.  (note: you will always get i==1 on a single proc machine).

Pages 19-20 in https://www.microsoft.com/whdc/driver/kernel/MP_issues.mspx explain why.  What is happening is that in about 1 in 1000 cases (if you actually test this on a multi-CPU machine), the CPU for Thread1 will preload the value of b2 at the same time the CPU for Thread2 is preloading the value for b1.  These CPUs then write out the new values at the same time, but lo and behold.  The loaded values for each bool will be false and neither if statement will be executed.  Pretty interesting, yes?

On the "completely unrelated, but important" front, I have switched teams within Microsoft and I now belong to the .Net Client team (we own System.Windows.Forms, ComponentModel, Designer and a whole bunch of new features for whidbey such as ToolStrips, LayoutPanels, and DataGridView).  After using .Net for five years now, I hardly remember what MFC was like.  I can't imagine calling DoDataExchange or writing custom message handlers for anything.  I'm not sure many MFC coders know not only how easy it is to write UI in .Net, but also how flexible it is.  Only thing is -- I wish you could just override a method and see every window message being sent to the window.  Guess what-  you can do that!  Just override protected WndProc and you have all the power in the world.  Man, imagine having to add message handlers into a table (using defines you must remember beforehand) for each message you wanted and pointing to a new method.  What a hassle.  I think its almost too easy in .Net.  Programmers in .Net have a tendency to be wasteful and create GDI objects (such as Fonts or Brushes) without cleaning them up -- but I digress.

On the "be careful, don't do that" front, do not call SetWindowLong(GWL_WNDPROC) on a background thread (that is -- a thread other than the one that owns the window handle).  There is a threading issue with DispatchMessage and SetWindowLong(GWL_WNDPROC) that could cause your app to AV.  To change the wndproc or subclass/unsubclass you should do this via a window message so the SetWindowLong call gets executed on the UI thread -- or -- ensure that DispatchMessage is not being called on the window handle you are changing the wndproc.  Support article on this is incoming soon.

And finally, on the "be careful and DO that" front, please remember to call Dispose on all disposable objects.  If you do not call dispose explicitly, you will allow the object to be garbage collected.  Not only does this decrease performance because your peak workingset is higher, but you are also exposing yourself to potentially nasty threading issues because, yes, the finalizer runs on a background thread.  (see previous 2 issues for examples).  Finalizers also typically have different logic than the explicit dispose and finalizers are difficult to write not only because of the threading issues -- but also because finalization order is not guaranteed.  For example, if A references B and B references C and C references A which one will be finalized first when none of the three are referenced by outside objects?  The answer is that it is not deterministic -- and you need to remember that when writing your finalizer.  More on this later.

That's it for now -- happy Mother's day!

Comments

  • Anonymous
    May 19, 2005
    Jack asks:

    I couldn't find where to post the comment for the cross threading problem. While the solution...