objective-c中的线程安全单例对象?

时间:2012-01-06 11:21:45

标签: iphone objective-c thread-safety singleton

如何在objective-c中创建线程安全的单例对象。例如。如果我有一个共享数据控制器是一个单例对象,如果两个或多个线程同时访问它会发生什么?或者这些对象默认具有线程安全性。?

更新

在这种情况下,datacontroller属性中的对象是否决定它是否是线程安全的?就像我的datacontroller有NSMutableArray并且它设置为nonatomic它不会是线程安全的。在这种情况下,它的价值会发生什么?

更新:2

实际上@synchronized(self)做了什么..?

2 个答案:

答案 0 :(得分:5)

如果数据控制器不是线程安全的,那么可能会发生未定义的行为 - 不惜一切代价避免它=)

默认情况下,NSObjects绝对不是线程安全。使用原子属性不会使类线程安全(添加因为它是一种流行的误解)。

典型的解决方案是确保所有可变状态都受到适当锁定的保护(例如互斥锁或@synchronized)。

当我说可变状态时,我指的是一个可以在外部或内部发生变异的对象。如果您不确定,请锁定以确保从多个线程读取或写入类型。这必须发生在阅读和写作 - 总是。如果你有很多阅读,一个readwrite锁可能是一个更好,更专业的锁。

要更详细地回答,您必须发布一些代码。

<强>更新

  

在这种情况下,datacontroller属性中的对象是否决定它是否是线程安全的?就像我的datacontroller有NSMutableArray并且它设置为nonatomic它不会是线程安全的。在这种情况下,它的价值会发生什么?

将其视为具有传递性。你的NSMutableArray,它拥有的对象以及它们的所有外部引用必须以线程安全的方式使用,你必须跟踪所有这些。通常,您首先要减少共享的可变状态。而不是给客户端提供对数组的引用,而是为它们提供数组所拥有的元素的副本。同时,使用锁保护所有读取,写入和元素复制。

为简单起见,我将演示使用@synchronize

@interface MONCookie : NSObject <NSCopying>

- (NSString *)name;

@end

@interface MONDataController : NSObject
{
@private
  NSMutableArray * cookies; // << MONCookie[]
}

- (void)addCookie:(MONCookie *)cookie;

- (MONCookie *)cookieWithName:(NSString *)name;

@end

@implementation MONDataController

- (id)init
{
  // no lock required here
  self = [super init];
  if (nil != self) {
    cookies = [NSMutableArray new];
  }
  return self;
}

- (void)dealloc
{
  // no lock required here
  [cookies release], cookies = nil;
  [super dealloc];
}

- (void)addCookie:(MONCookie *)cookie
{
  @synchronized(self) { // now accessing cookies - lock required
    [cookies addObject:cookie];
  }
}

- (MONCookie *)cookieWithName:(NSString *)name
{
  MONCookie * ret = nil;
  @synchronized(self) { // now accessing cookies - lock required
    for (MONCookie * at in cookies) {
      if ([at.name isEqualToString:name]) {
        ret = [at copy]; // << give them a copy if cookie is not threadsafe
      }
    }
  }
  return [ret autorelease];
}

@end

更新2

@synchronized设置对象级锁定。您可以将其视为您的实例独有的递归(或可重入)锁。与其他锁定方法相比,它也很慢。上面的代码使用它,它是线程安全的,等同于持有递归锁,并在@synchronized边界处锁定和解锁。

@interface MONCookie : NSObject <NSCopying>
{
@private
    NSRecursiveLock * lock;
}

@end

@implementation MONCookie

- (id)init
{
    self = [super init];
    if (nil != self) {
        lock = [NSRecursiveLock new];
    }
    return self;
}

- (void)temperatureDidIncrease
{
    // ...  
}

- (void)bake
{
    // use the same lock for everything
    // do not mix @synchronized in some places, and use of the lock 
    // in others. what you use to protect the data must remain consistent
    //
    // These are equivalent approaches to protecting your data:

    {   // @synchronized:
        @synchronized(self) {
            [self temperatureDidIncrease];  
        }
    }

    {   // using the lock:
        [lock lock];
        [self temperatureDidIncrease];  
        [lock unlock];
    }
}

@end

答案 1 :(得分:-5)

我之前没有在目标C中编程 但据我所知,从操作系统课程, 你应该在你的单例对象中放置一个共享的布尔变量,如boolean isLocked 当每次线程尝试访问该类时,它应该执行以下操作。 因为我记得这被称为严格的交替:)很好的旧学校时代。

主题A:

if(!isLocked) {
isLocked = True;
Do the stuff
isLocked = False;
}