同步这个多线程代码(iOS / Cocoa)的正确方法是什么?

时间:2017-10-29 03:43:22

标签: ios objective-c multithreading cocoa-touch nsthread

我们说我有NSMutableArray个对象(NSMutableArray 线程安全),我在包含此对象的对象上有这些方法数组(为了清楚起见,这是一个简化的例子):

- (void)addObject:(id)object {
    if (_objectsArray == nil) {
        _objectsArray = [NSMutableArray array];
    }

    [_objectsArray addObject:object];

    if (_thread == nil) {
        _thread = [[NSThread alloc] initWithTarget:self selector:@selector(__threadEntry:) object:nil];
        _thread.name = @"com.company.ThreadName";
        [_thread start];
    }
}

- (void)removeObject:(id)object {
    [_objectsArray removeObject:object];

    if (_objectsArray.count == 0) {
        _isRunning = NO;
    }
}

- (void)stopRendering {
    _isRunning = NO;
}

- (void)__threadEntry:(id)sender {
    // Set up CADisplayLink on current run loop.

    // "_isRunning" is declared as a "volatile BOOL"
    _isRunning = YES;
    while (_isRendering) {
        [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    // Thread cleanup.
}

- (void)__threadProc {
    @autoreleasepool {
        [_objectsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // Do work
        }];
    }
}

基本上,我有从可变数组中添加/删除对象的方法,但是在不同的线程上执行数组中对象的工作。即addObjectremoveObject都只是从主线程调用,而工作(在__threadProc中)是在不同的线程上完成的。

实际上,此代码不是线程安全的,因为在__threadProc中进行枚举时可以添加/删除对象。那么同步这个的正确方法是什么?

我不确定锁定是否是正确答案,因为锁定是否适用于不同的方法?例如,如果我在[_objectsArray addObject:object]方法中围绕addObject进行锁定/解锁,并在__threadProc中围绕工作进行锁定/解锁,那么这是否有用(当然假设两者都是相同的锁定对象(例如NSLock)?

此外,与__threadProc中的工作频率相比,添加/删除对象的频率很低。

1 个答案:

答案 0 :(得分:0)

假设我们在Objective-C中实现了一个线程安全的队列。我们可以这样开始:

@implementation ThreadSafeQueue
{
   NSMutableArray * _objectsArray;
   NSLock *_lock;
}

- (instancetype)init
{
    self = [super init];
     if (self) {
    _objectsArray = [NSMutableArray array];
    _lock = [[NSLock alloc] init];
 }
  return self;
}

- (void)push:(id)object
{
   [_lock lock];
   [_objectsArray addObject:object];
   [_lock unlock];
}

// Or using the @synchronized construct:

 @synchronized (self) {
    [_elements addObject:element];
  }
@end

上面的ThreadSafeQueue类有一个初始化两个ivars的init方法:一个_objectsArray数组和一个NSLock。它有一个push:方法获取锁,将_object插入到数组中,然后释放锁。许多线程可以同时调用push:但是行[_objectsArray addObject:object]一次只能在一个线程上运行。步骤可能是这样的:

 Thread A calls the push: method
 Thread B calls the push: method
 Thread B calls [_lock lock] - since nobody else is holding the lock, 
 Thread B acquires the lock
 Thread A calls [_lock lock] but the lock is held by Thread B so the method call doesn’t return - this pauses execution in thread A
 Thread B adds its objects to _objectsArray and calls [_lock unlock]. When this happens, Thread A’s [_lock lock] method returns and it goes on to insert its own object

我们可以使用@synchronized构造更简洁地实现它:

同步块与上例中的[_lock lock]和[_lock unlock]具有相同的效果。您可以将其视为锁定自我,就好像自我是一个NSLock。在打开{运行之后的任何代码之前,并且在关闭之后的任何代码之前释放锁定}之前,锁定是有效的。这真的很方便,因为这意味着你永远不会忘记打电话解锁!

//或者使用@synchronized构造:

 @synchronized (self) {
    [_elements addObject:element];
  }

您可以在任何Objective-C对象上进行@synchronize。所以我们也可以在上面的例子中使用@synchronized(_elements)而不是@synchronized(self),效果也一样。