chi*_*fet 11 objective-c thread-safety ios
我正在阅读关于线程安全的Apple文档,而且我并不完全清楚(在实践中)真正构成一个线程安全的类.为了更好地理解这一点,为了使线程安全(以及为什么),将需要对下面的类进行什么操作?
#import "UnsafeQueue.h"
@interface UnsafeQueue()
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation UnsafeQueue
- (id)peek {
return [self.data firstObject];
}
- (NSUInteger)length {
return [self.data count];
}
- (void)enqueue:(id)datum {
[self.data addObject:datum];
}
// other methods omitted...
@end
Run Code Online (Sandbox Code Playgroud)
只需创建一个ivar NSLock,然后锁定/解锁与底层NSMutableArray的所有交互?
长度方法,只是要求数组的计数,也需要这样做吗?
Rob*_*ier 23
使类线程安全的最简单和最好的方法是使其不可变.那你就不必处理任何这个了.它只是有效.值得花时间思考是否需要在多个线程上进行可变性.
但是如果不可变类为您的设计带来了重大问题,那么通常最好的实现方法是使用GCD而不是锁.GCD的开销要低得多,而且通常说起来更容易.
在这个特殊情况下,我会按照这些方式实现它(未经测试,我已经在Swift中工作了一段时间,所以如果我删除分号,请原谅我):
#import "SafeQueue.h"
@interface SafeQueue()
@property (strong, nonatomic) NSMutableArray *data;
@property (strong, nonatomic) dispatch_queue_t dataQueue;
@end
@implementation SafeQueue
- (instancetype)init {
if (self = [super init]) {
_dataQueue = dispatch_queue_create("SafeQueue.data", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (id)peek {
__block id result = nil;
dispatch_sync(self.dataQueue, ^{ result = [self.data firstObject] });
return result;
}
- (NSUInteger)length {
__block NSUInteger result = 0;
dispatch_sync(self.dataQueue, ^{ result = [self.data count] });
return result;
}
- (void)enqueue:(id)datum {
dispatch_barrier_async(self.dataQueue, ^{ [self.data addObject:datum] });
}
// other methods omitted...
@end
Run Code Online (Sandbox Code Playgroud)
请注意dispatch_sync所有读者和dispatch_barrier_async所有作者的使用.这是通过允许并行读取器和独占编写器将开销降至最低的方法.如果没有争(这是正常的情况下),dispatch_sync比锁(低得多的开销NSLock或者@synchronized甚至并行线程锁).
有关如何在Cocoa中更好地处理并发性的更多建议,请参阅从线程迁移.
线程安全意味着可以通过多个线程访问和/或修改数据结构而不会损坏.
一种简单的方法是使用Objective-C的@synchronized功能.
在这种情况下,@synchronized(self.data)围绕对阵列的所有访问将确保一次只有一个线程可以访问该阵列.
即使length不修改数组,您仍然需要保护其访问权限,因为另一个线程可能会修改该数组 -
#import "SafeQueue.h"
@interface SafeQueue()
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation SafeQueue
- (id)peek {
@synchronized (self.data) {
return [self.data firstObject];
}
}
- (NSUInteger)length {
@synchronized(self.data) {
return [self.data count];
}
}
- (void)enqueue:(id)datum {
@synchronized(self.data) {
[self.data addObject:datum];
}
}
// other methods omitted...
@end
Run Code Online (Sandbox Code Playgroud)
最重要的是,线程安全的第一步是确保在尝试从另一个线程访问对象时,没有一个线程使对象变异(使其处于可能不一致的状态).因此,任何各种同步技术都是有用的.有关各种类型机制的详细信息,请参阅" 线程编程指南"的" 同步"部分.Rob Napier演示的读写器模式远比指令更有效,并且在WWDC 2012视频异步设计模式与块,GCD和XPC中进行了讨论.@synchronizedNSLock
顺便说一句,在peek和length方法在多线程环境下已经减少的效用.这些暗示了天真的开发人员可能错误地推断这些方法和其他方法之间的依赖关系.例如,仅仅因为length大于零并不意味着当你随后去检索它时,任何东西都会存在.
我会仔细研究这些方法,并问自己在多线程环境中是否有意义.我知道你可能只是意味着这些是可变数组中线程安全的任意例子,但它表明了我在"线程安全"示例中经常看到的更广泛的问题,我在其他地方看到Stack Overflow,其中同步机制通常是在错误的水平上承担任何效用.
| 归档时间: |
|
| 查看次数: |
5949 次 |
| 最近记录: |