Thread synchronization

Sean Kelly sean at f4.ca
Wed Jan 3 12:22:00 PST 2007


Matthew Wesley wrote:
> On Thu, 30 Nov 2006 13:04:48 +0100
> xs0 <xs0 at xs0.com> wrote:
> 
>> Sean Kelly wrote:
>>> JohnC wrote:
>>>> What's the recommended way to guard static variables?
>>>>
>>>> 1) synchronized by itself?
>>>>
>>>>   static Object getObj() {
>>>>     synchronized {
>>>>       return sharedVar;
>>>>     }
>>>>   }
>>>>
>>>> 2) synchronized on classinfo?
>>>>
>>>>   static Object getObj() {
>>>>     synchronized (typeof(sharedVar).classinfo) {
>>>>       return sharedVar;
>>>>     }
>>>>   }
>>>>
>>>> 3) synchronized on a static Object instance?
>>>>
>>>>   static this() {
>>>>     syncLock = new Object;
>>>>   }
>>>>
>>>>   static Object getObj() {
>>>>     synchronized (syncLock) {
>>>>       return sharedVar;
>>>>     }
>>>>   }
>>>>
>>>> I've seen all these patterns used. Are they all safe?
>>> They're all safe.  Since #1 is in a static method, it will
>>> synchronize on the ClassInfo object, just like you're doing
>>> manually in #2.  And #3 just specifies a separate static object on
>>> which to synchronize.  But since it's static, you'll get the same
>>> effect.
>> I think the first one synchronizes only on the statement itself, so 
>> while only one getObj can execute concurrently, it doesn't guard
>> against a similar setObj()..
>>
>> xs0
> 
> Perhaps I am misunderstanding the synchronized specification, as none
> of these methods seem safe to me. When I read that "Synchronized allows
> only one thread at a time to execute ScopeStatement" from
> http://www.digitalmars.com/d/statement.html#SynchronizedStatement, that
> seems to me that only the return statement is protected in any of these
> cases, while any modification of the returned object is unsynchronized.

This is correct.  Given:

     synchronized(obj) { ... }

You can consider the opening brace of the synchronized block to be 
equivalent to a mutex lock operation where obj is the mutex being 
locked.  Exiting the scope of a synchronized block by any means releases 
the lock.

> This seems to be what xs0 mentions above about #1, but how are #2 and
> #3 any different?

In #1, the mutex being used is implicit while in #2 and #3 it's 
explicit.  In all cases however, the programmer is responsible for 
locking all related functions on the proper object.

> On a related note,, the specification says that "where Expression
> evaluates to an Object reference, allows only one thread at a time to
> use that Object to execute the ScopeStatement." Doesn't this mean that
> there is no way to synchronize separate critical sections? For example,
> two or more critical sections that would block on the same mutex in a C
> environment.

Well, these two functions lock on the same mutex:

     class C
     {
         void f1()
         {
             synchronized
             {
                 ...
             }
         }

         void f2()
         {
             synchronized
             {
                 ...
             }
         }

         static void f3()
         {
             synchronized
             {
                 ...
             }
         }

         static void f4()
         {
             synchronized
             {
                 ...
             }
         }
     }

     C val = new C;
     // f1 and f2 are mutually exclusive
     val.f1();
     val.f2();
     // f3 and f4 are mutually exclusive
     val.f3();
     val.f4();

In this case, the mutex used for f1 and f2 is the object monitor for 
val, while the mutex used for f3 and f4 is the object monitor for C. 
This is equivalent to:

     class C
     {
         Object lock;
         static Object s_lock;

         this()
         {
             lock = new Object;
         }

         static this()
         {
             s_lock = new Object;
         }

         void f1()
         {
             synchronized(lock)
             {
                 ...
             }
         }

         void f2()
         {
             synchronized(lock)
             {
                 ...
             }
         }

         static void f3()
         {
             synchronized(s_lock)
             {
                 ...
             }
         }

         static void f4()
         {
             synchronized(s_lock)
             {
                 ...
             }
         }
     }

     C val = new C;
     // f1 and f2 are mutually exclusive
     val.f1();
     val.f2();
     // f3 and f4 are mutually exclusive
     val.f3();
     val.f4();

Does that help?


Sean


More information about the Digitalmars-d-learn mailing list