22 多線程之a(chǎn)tomic雾家,讀寫(xiě)安全

一 atomic

atomic用于保證屬性setter、getter的原子性操作寞埠,相當(dāng)于在gettersetter內(nèi)部加了線程同步的鎖

可以參考源碼objc4的objc-accessors.mm

  • setter方法實(shí)現(xiàn)
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}

  • get方法實(shí)現(xiàn)
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    if (offset == 0) {
        return object_getClass(self);
    }

    // Retain release world
    id *slot = (id*) ((char*)self + offset);
    if (!atomic) return *slot;

    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();

    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}

分析

nonatomicatomic

  • atom:原子摩骨,不可再分割的單位
  • atomic:原子性
  • 給屬性加上atomic修飾通贞,可以保證屬性settergetter都是原子性操作,也就是保證settergette內(nèi)部是線程同步
@property (copy, atomic) NSString *name;

- (void)setName:(NSString *)name {
    // 加鎖
    _name = name;
    // 解鎖
}

- (NSString *)name {
// 加鎖
    return _name;
// 解鎖
}

  • 它并不能保證使用屬性的過(guò)程是線程安全的
@property (strong, atomic) NSMutableArray *data;

Person *p = [[Person alloc] init];
p.data = [NSMutableArray array];

// 以下操作就不能保證線程安全了
for (int i = 0; i < 10; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [p.data addObject:@"1"];
    });
}

雖然data屬性是聲明為atomic,但是也只是在p.data(實(shí)際上調(diào)用了get方法)和p.data = [NSMutableArray array];(實(shí)際上調(diào)用了set方法)是安全的恼五。但是多條線程同時(shí)添加對(duì)象時(shí)昌罩,即[p.data addObject:@"1"];并不能保證線程安全。

二 讀與寫(xiě)的方案

思考如何實(shí)現(xiàn)以下場(chǎng)景

  • 同一時(shí)間灾馒,只能有1個(gè)線程進(jìn)行寫(xiě)的操作
  • 同一時(shí)間茎用,允許有多個(gè)線程進(jìn)行讀的操作
  • 同一時(shí)間,不允許既有寫(xiě)的操作睬罗,又有讀的操作

上面的場(chǎng)景就是典型的“多讀單寫(xiě)”轨功,經(jīng)常用于文件等數(shù)據(jù)的讀寫(xiě)操作,iOS中的實(shí)現(xiàn)方案有

  • pthread_rwlock:讀寫(xiě)鎖
  • dispatch_barrier_async:異步柵欄調(diào)用
2.1 pthread_rwlock

等待鎖的線程會(huì)進(jìn)入休眠

1653926-c5785b8c58338b76.png

代碼例子如下

#import <pthread.h>
@property (assign, nonatomic) pthread_rwlock_t lock;

- (void)viewDidLoad {
    [super viewDidLoad];

    // 初始化鎖
    pthread_rwlock_init(&_lock, NULL);

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            [self read];
        });
        dispatch_async(queue, ^{
            [self write];
        });
    }
}

- (void)read {
    pthread_rwlock_rdlock(&_lock);

    sleep(1);
    NSLog(@"%s", __func__);

    pthread_rwlock_unlock(&_lock);
}

- (void)write {
    pthread_rwlock_wrlock(&_lock);

    sleep(1);
    NSLog(@"%s", __func__);

    pthread_rwlock_unlock(&_lock);
}

- (void)dealloc {
    pthread_rwlock_destroy(&_lock);
}

執(zhí)行結(jié)果

1653926-f9753ce765525426.png
2.2 dispatch_barrier_async
  • 這個(gè)函數(shù)傳入的并發(fā)隊(duì)列必須是自己通過(guò)dispatch_queue_cretate創(chuàng)建的
  • 如果傳入的是一個(gè)串行或是一個(gè)全局的并發(fā)隊(duì)列容达,那這個(gè)函數(shù)便等同于dispatch_async函數(shù)的效果

重要方法

// 初始化隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
// 讀
dispatch_async(self.queue, ^{
    [self read];
});
// 寫(xiě)
dispatch_barrier_async(self.queue, ^{
    [self write];
});

原理如下

1653926-d9f78ddc974b7c29.png

代碼例子如下

@property (strong, nonatomic) dispatch_queue_t queue;

// 柵欄
- (void)barrier_async_test {
    self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);

    for (int i = 0; i < 5; i++) {
        dispatch_async(self.queue, ^{
            [self read];
        });

        dispatch_async(self.queue, ^{
            [self read];
        });

        dispatch_barrier_async(self.queue, ^{
            [self write];
        });
    }
}

- (void)read {
    sleep(1);
    NSLog(@"read");
}

- (void)write {
    sleep(1);
    NSLog(@"write");
}

執(zhí)行結(jié)果如下

1653926-a7ccf12317e97a22.png

本文參考:
路飛_Luck (http://www.reibang.com/p/07f7b96bb03f)
以及借鑒MJ的教程視頻
非常感謝.


項(xiàng)目源碼 - GCD-讀寫(xiě)安全
項(xiàng)目源碼 - GCD-automic

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末古涧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子花盐,更是在濱河造成了極大的恐慌羡滑,老刑警劉巖菇爪,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柒昏,居然都是意外死亡凳宙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)昙楚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)近速,“玉大人,你說(shuō)我怎么就攤上這事堪旧∠鞔校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵淳梦,是天一觀的道長(zhǎng)析砸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)爆袍,這世上最難降的妖魔是什么首繁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮陨囊,結(jié)果婚禮上弦疮,老公的妹妹穿的比我還像新娘。我一直安慰自己蜘醋,他們只是感情好胁塞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著压语,像睡著了一般啸罢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胎食,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天扰才,我揣著相機(jī)與錄音,去河邊找鬼厕怜。 笑死衩匣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粥航。 我是一名探鬼主播舵揭,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼躁锡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起置侍,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤映之,失蹤者是張志新(化名)和其女友劉穎拦焚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體杠输,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赎败,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蠢甲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僵刮。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鹦牛,靈堂內(nèi)的尸體忽然破棺而出搞糕,到底是詐尸還是另有隱情,我是刑警寧澤曼追,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布窍仰,位于F島的核電站,受9級(jí)特大地震影響礼殊,放射性物質(zhì)發(fā)生泄漏驹吮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一晶伦、第九天 我趴在偏房一處隱蔽的房頂上張望碟狞。 院中可真熱鬧,春花似錦婚陪、人聲如沸族沃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)竭业。三九已至,卻和暖如春及舍,著一層夾襖步出監(jiān)牢的瞬間未辆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工锯玛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咐柜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓攘残,卻偏偏與公主長(zhǎng)得像拙友,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歼郭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容