@interface ViewController ()
@property (nonatomic, strong) NSString *someString;
@end

@implementation ViewController

@synthesize someString = _someString;

- (NSString *)someString {
    __block NSString *tmp;
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        tmp = _someString;
    });
    return tmp;
}

- (void)setSomeString:(NSString *)someString {
    __block NSString *tmp;
    dispatch_barrier_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        tmp = someString;
    });
    _someString = tmp;
}
@end

有人说它比@synchronized更好,因为所有锁定都在GCD中进行了处理。

最佳答案

首先,您的设置程序毫无意义,使用默认的并发队列也可能不是您想要的。您的代码应该看起来更像:

@interface ViewController ()
@property (nonatomic, copy) NSString *someString;
@end

@implementation ViewController
{
    dispatch_queue_t _stateGuardQueue;
}

- (instancetype)init
{
    if (self = [super init])
    {
        _stateGuardQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

@synthesize someString = _someString;

- (NSString *)someString {
    __block NSString *tmp;
    dispatch_sync(_stateGuardQueue, ^{
        tmp = _someString;
    });
    return tmp;
}

- (void)setSomeString:(NSString *)someString {
    NSString* tmp = [someString copy];
    dispatch_barrier_async(_stateGuardQueue, ^{
        _someString = tmp;
    });
}

@end

我所做的更改:
  • 使设置员实际上在关键部分
  • 内进行突变
  • 使用按实例的私有(private)并发队列,而不是全局默认并发队列;将屏障块提交给默认的并发队列并没有达到您的预期。 (请参阅docs)
  • dispatch_barrier_sync更改为dispatch_barrier_async,同步等待setter块完全没有意义,因为无法在当前线程上读取陈旧的数据。
  • 将属性更改为具有copy语义,这对于值语义类型(NSString等)始终是一种好习惯,这在从多个线程同时读取属性的情况下尤其重要。

  • 要知道的是,孤立地讲,此模式所提供的“安全性”不比原子属性高,因此可以说,您应该只使用那些(较少的代码等)。对于性能问题,是的,对于这种特殊用途,GCD肯定会胜过@synchronized。首先,它允许并发读取,其中@synchronized将序列化并发读取。如果不进行测试,我希望原子属性的性能均优于两者。那种原子性以及通常以这种方式保护单个操作很少是一种适当的并发策略。

    09-09 21:39
    查看更多