Потокобезопасность - лениво инициализированное свойство getter - Цель C - PullRequest
0 голосов
/ 26 июня 2018

Предположим, что если я хочу реализовать и геттер, и сеттер, я сделаю так -

@interface Person () {
 dispatch_queue_t _myConcurrentQueue;
}

@property (nonatomic, copy) NSString *personName;

@end

@implementation Person


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

@synthesize personName = _personName;

- (NSString *)personName {
    __block NSString *tmp;
    dispatch_sync(_myConcurrentQueue, ^{
        tmp = _personName;
    });
    return tmp;
}

- (void)setpersonName:(NSString *)personName {
    NSString* tmp = [personName copy];
    dispatch_barrier_async(_myConcurrentQueue, ^{
        _personName = tmp;
    });
}

@end

Но если я хочу лениво инициализировать свое свойство, то как я могу сделать его потокобезопасным? Пример -

- (NSString *)personName {
        If (!_personName) {
           _personName = "Some Name"
         }

    return _personName;
}

Что я должен использовать последовательная очередь с dispatch_async или параллельная очередь с dispatch_async барьер и почему?

1 Ответ

0 голосов
/ 27 июня 2018

Ну, в вашем тривиальном случае с использованием строкового литерала (хотя вы пренебрегли @), у вас мало шансов ошибиться.

Однако для общего случая, когда вы инициализируете переменную экземпляра более динамичным способом с возможными побочными эффектами, нет, это не поточно-ориентированный. Два потока могут быть одновременно в геттере. Каждый из них может отправить задачу в очередь. Поскольку очередь является параллельной, и ни одна задача не была представлена ​​в качестве барьера, обе задачи могут выполняться одновременно. Они оба могут проверить !_personName и оба определят, что им нужно его инициализировать. Они будут выполнять избыточную работу, и, если у нее есть побочные эффекты, это может испортить ситуацию.

Также возможно, что компилятор и / или процессор могут изменить порядок действий. Один поток может подумать, что _personName не является nil и вернуть указатель, даже если другой поток все еще работает над инициализацией объекта. То есть присваивание _personName может произойти до того, как все эффекты выражения в правой части присваивания завершатся и станут видимыми для другого потока.

...