Singletons and Threads

  • If I have a class that is a singleton, and create the first instance in a
    thread using a standard [sharedInstance] method, will calling
    [sharedInstance] in another thread use that single instance, or will each
    thread get its own instance of the class?
    Searching about, I can't find an explicit answer to this question.
  • um, well it rather depends how you've written your singleton code
    surely? Unless you've explicitly written the code to have different
    instances per-thread, it should always be the same.

    However, the thing to be wary of is that 2 threads both call
    +sharedInstance for the first time, and at about the same time, you
    could end up creating 2 instances, one of which would then be leaked.
    So make sure your code has a proper lock around it.

    Mike.

    On 14 Sep 2007, at 11:45, Randall Wood wrote:

    > If I have a class that is a singleton, and create the first
    > instance in a
    > thread using a standard [sharedInstance] method, will calling
    > [sharedInstance] in another thread use that single instance, or
    > will each
    > thread get its own instance of the class?
    > Searching about, I can't find an explicit answer to this question.
  • On 9/14/07, Randall Wood <randall.h.wood...> wrote:
    > If I have a class that is a singleton, and create the first instance in a
    > thread using a standard [sharedInstance] method, will calling
    > [sharedInstance] in another thread use that single instance, or will each
    > thread get its own instance of the class?
    > Searching about, I can't find an explicit answer to this question.

    That all depends on how you've written your class. You could write it
    to function either way. Odds are, if you've written it the most
    straightforward way (i.e. storing a pointer to the singleton in a
    static variable), all threads will share the same instance.

    As a side note, be sure that you've adequately protected your
    sharedInstance method from threading problems. It would be best if you
    posted your code, without that, any answer you get will just be
    conjecture.

    --
    Clark S. Cox III
    <clarkcox3...>
  • This is the code I have for ensuring that the class is a singleton:

    static MPInterpreter *sharedMPInterpreter = nil;

    @implementation MPInterpreter

    - (id) init {

    if (self = [super init]) {

    _interpreter = Tcl_CreateInterp();

    if(_interpreter == NULL) {

    NSLog(@"Error in Tcl_CreateInterp, aborting.");

    return Nil;

    }

    if(Tcl_Init(_interpreter) == TCL_ERROR) {

    NSLog(@"Error in Tcl Init: %s", Tcl_GetStringResult(_interpreter));

    return Nil;

    }

    if( Tcl_EvalFile(_interpreter, [[[NSBundle mainBundle] pathForResource:
    @"init" ofType:@"tcl"] UTF8String]) != TCL_OK) {

    NSLog(@"Error in Tcl_EvalFile: %s", Tcl_GetStringResult(_interpreter));

    return Nil;

    }

    }

    return self;

    }

    + (MPInterpreter*)sharedInterpreter {

    @synchronized(self) {

    if (sharedMPInterpreter == nil) {

    [[self alloc] init]; // assignment not done here

    }

    }

    return sharedMPInterpreter;

    }

    + (id)allocWithZone:(NSZone*)zone {

    @synchronized(self) {

    if (sharedMPInterpreter == nil) {

    sharedMPInterpreter = [super allocWithZone:zone];

    return sharedMPInterpreter; // assignment and return on first allocation

    }

    }

    return nil; // subsequent allocation attempts return nil

    }

    - (id)copyWithZone:(NSZone*)zone {

        return self;

    }

    - (id)retain {

        return self;

    }

    - (unsigned)retainCount {

        return UINT_MAX;  //denotes an object that cannot be released

    }

    - (void)release {

        //do nothing

    }

    - (id)autorelease {

        return self;

    }

    It is almost straight from an example in (I think) the Apple Developer site.
    I would like to be able to have code that ensures that a only a single
    instance exists within each thread.

    On 9/14/07, Clark Cox <clarkcox3...> wrote:
    >
    > On 9/14/07, Randall Wood <randall.h.wood...> wrote:
    >> If I have a class that is a singleton, and create the first instance in
    > a
    >> thread using a standard [sharedInstance] method, will calling
    >> [sharedInstance] in another thread use that single instance, or will
    > each
    >> thread get its own instance of the class?
    >> Searching about, I can't find an explicit answer to this question.
    >
    > That all depends on how you've written your class. You could write it
    > to function either way. Odds are, if you've written it the most
    > straightforward way (i.e. storing a pointer to the singleton in a
    > static variable), all threads will share the same instance.
    >
    > As a side note, be sure that you've adequately protected your
    > sharedInstance method from threading problems. It would be best if you
    > posted your code, without that, any answer you get will just be
    > conjecture.
    >
    >
    > --
    > Clark S. Cox III
    > <clarkcox3...>
    >

    --
    Randall Wood
    <randall.h.wood...>

    "The rules are simple: The ball is round. The game lasts 90 minutes. All the
    rest is just philosophy."
  • On 9/14/07, Randall Wood <randall.h.wood...> wrote:
    > This is the code I have for ensuring that the class is a singleton:
    >
    > static MPInterpreter *sharedMPInterpreter = nil;
    >
    >
    > @implementation MPInterpreter
    >
    >
    > - (id) init {
    >
    > if (self = [super init]) {
    >
    > _interpreter = Tcl_CreateInterp();
    >
    > if(_interpreter == NULL) {
    >
    > NSLog(@"Error in Tcl_CreateInterp, aborting.");
    >
    > return Nil;
    >
    > }

    Your error pathways in your init method will leak the instance being
    inited. If you are going to return nil you need to send yourself a
    release message first.

    Also nothing in the code posted ensures that only one will exist in a
    given thread. To do that consider using -[NSThread threadDictionary]
    to store a reference to the thread scoped instance.

    -Shawn
  • >
    > + (MPInterpreter*)sharedInterpreter {
    >
    > @synchronized(self) {
    >
    > if (sharedMPInterpreter == nil) {
    >
    > [[self alloc] init]; // assignment not done here
    >
    > }
    >
    > }
    >
    > return sharedMPInterpreter;
    >
    > }

    You are not assigning the newly inited object to the
    sharedMPInterpreter, so you are in fact returning nil. Leaving aside
    the @synchronized bit, I would have written it like this:

    + (MPInterpreter *) sharedInterpreter
    {
        static MPInterpreter    *sharedInstance = nil;

        if (sharedInstance == nil)
        {
      sharedInstance = [[[self class] alloc] init];
        }

        return sharedInstance;
    }

    Cheers, Patrick
  • On Sep 14, 2007, at 6:49 PM, PGM wrote:

    >> You are not assigning the newly inited object to the
    >> sharedMPInterpreter, so you are in fact returning nil. Leaving
    >> aside the @synchronized bit, I would have written it like this:
    >
    > + (MPInterpreter *) sharedInterpreter
    > {
    > static MPInterpreter    *sharedInstance = nil;
    >
    > if (sharedInstance == nil)
    > {
    > sharedInstance = [[[self class] alloc] init];
    > }
    >
    > return sharedInstance;
    > }

    You also need synchronization, to ensure that the shared instance is
    only created once.

    + (MPInterpreter *)sharedInterpreter {
        static MPInterpreter *sharedInterpreter = nil;

        @synchronized (self) {
            if (sharedInstance == nil) {
                sharedInstance = [[[self class] alloc] init];
            }
        }

        return sharedInterpreter;
    }

    The other advantage of this is that you can avoid the wacky override
    of +allocWithZone:, thus allowing your code to support instantiating
    new, individual MPInterpreter instances in your unit tests.

    Seriously, the "I want to enforce singleton-hood" pattern is generally
    something to be avoided.  You're usually better off just providing a
    +sharedBlah method and a comment in your class header file (along with
    no designated initializer) than trying to force there to only ever be
    one instance of a particular class.

      -- Chris
  • On 9/14/07, Chris Hanson <cmh...> wrote:
    > On Sep 14, 2007, at 6:49 PM, PGM wrote:
    >
    >>> You are not assigning the newly inited object to the
    >>> sharedMPInterpreter, so you are in fact returning nil. Leaving
    >>> aside the @synchronized bit, I would have written it like this:
    >>
    >> + (MPInterpreter *) sharedInterpreter
    >> {
    >> static MPInterpreter      *sharedInstance = nil;
    >>
    >> if (sharedInstance == nil)
    >> {
    >> sharedInstance = [[[self class] alloc] init];
    >> }
    >>
    >> return sharedInstance;
    >> }
    >
    > You also need synchronization, to ensure that the shared instance is
    > only created once.

    You folks aren't look at the rest of the code he posted. He doesn't
    need to do any assignment in that block or do sync in that block. Look
    at allocWithZone: to see why.

    This is basically Apple's example for how to do an "enforced" singleton...

    <http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals
    /CocoaObjects/chapter_3_section_10.html
    >

    -Shawn
  • If I recall correctly when using @synchronized() you should use a
    *specific* object, not a variable such as self. I usually write my
    singleton class methods to use [MySingletonClass class] as the
    semaphore.

    - Keith
  • > If I recall correctly when using @synchronized() you should use a
    > *specific* object, not a variable such as self. I usually write my
    > singleton class methods to use [MySingletonClass class] as the
    > semaphore.

    But self IS a specific object. In the context of a class method, self
    is the class object itself. So, synchronizing on self is the same as
    synchronizing on [self class], which is the same as synchronizing on
    [MySingletonClass class].

    In some other situations, yes, it is important to remember that
    @synchronized acts on the value of the pointer you pass it rather
    than just the name of the variable. For instance, this accessor would
    not be properly thread safe, because the value of the semaphore is
    actually changing within the critical code:

    - (void)setFoo:(id)newFoo;
    {
    @synchronized(_foo)
    {
      if (_foo == newFoo)
      return;
      [_foo release];
      _foo = [newFoo retain];
    }
    }
  • El 16/9/2007, a las 15:17, Milo Bird escribió:

    >> If I recall correctly when using @synchronized() you should use a
    >> *specific* object, not a variable such as self. I usually write my
    >> singleton class methods to use [MySingletonClass class] as the
    >> semaphore.
    >
    > But self IS a specific object. In the context of a class method,
    > self is the class object itself. So, synchronizing on self is the
    > same as synchronizing on [self class], which is the same as
    > synchronizing on [MySingletonClass class].

    Just to nitpick, there is one case where [self class] is not the same
    as [MySingletonClass class], and that is when working with
    subclasses: if the subclass calls "super" in a class method defined
    in the superclass then "self" will be a pointer to the instance of
    the subclass, not the superclass.

    So if you control things or know your singleton class will never be
    subclassed, then you know can treat "self" (when used in class
    methods) and [MySingletonClass self] as equivalent. But if you want
    to allow for the possibility of subclassing then you have to bear in
    mind this issue.

    Having said that, in my own code base I don't have any singleton
    classes that are designed with subclassing in mind. But conceivably
    it's something you could do.

    Cheers,
    Wincent
  • On 9/16/07, Keith Duncan <keith_duncan...> wrote:
    > If I recall correctly when using @synchronized() you should use a
    > *specific* object, not a variable such as self. I usually write my
    > singleton class methods to use [MySingletonClass class] as the
    > semaphore.

    In the case of +initialize, self *is* a specific object. It is the
    class itself. [MySingletonClass class] and self are the same thing.

    --
    Clark S. Cox III
    <clarkcox3...>
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today