[英]What is the correct way to synchronize this multithreaded code (iOS/Cocoa)?
假设我有一个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
}];
}
}
因此,基本上,我有一些方法可以从可变数组中添加/删除对象,但是对数组中对象的处理是在另一个线程上执行的。 即addObject
和removeObject
都只能从主线程调用,而工作(在__threadProc
)是在另一个线程上完成的。
实际上,此代码不是线程安全的,因为可以在__threadProc
进行枚举时添加/删除对象。 那么同步它的正确方法是什么?
我不确定锁是否是正确的答案,因为锁是否可以跨不同的方法工作? 例如,如果我在addObject
方法中对[_objectsArray addObject:object]
进行了锁定/解锁,而对__threadProc
的工作进行了锁定/解锁,那么该工作是否可行(假设两者都是同一个锁定对象(例如NSLock
)) ?
此外,与__threadProc
完成工作的频率相比,添加/删除对象的频率非常低。
假设我们正在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类具有一个init方法,该方法初始化两个ivars:_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]相同。 您可以将其视为对self的锁定,就好像self是一个NSLock。 在运行{之前的{}之后的任何代码之前先获得一个锁,并且在运行{}之后的任何代码之前●释放该锁。 这真的很方便,因为这意味着您永远不会忘记调用解锁!
//或使用@synchronized构造:
@synchronized (self) {
[_elements addObject:element];
}
您可以在任何Objective-C对象上@synchronize。 因此,在上面的示例中,我们也可以使用@synchronized(_elements)代替@synchronized(self),并且效果是相同的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.