多線程的安全問(wèn)題

多線程涉及到寫(xiě)操作就容易出現(xiàn)問(wèn)題

對(duì)指針本身賦值:

self.userName = @"peak";

訪問(wèn)指針指向的字符串所在的內(nèi)存區(qū)域:

[self.userName rangeOfString:@"peak"]

property分為三類:
pointer Property - > Memory
Primitive Property

內(nèi)存的理解:

我們只有一個(gè)地址總線,一個(gè)內(nèi)存拂蝎。即使在多線程的情況下穴墅,也不可能存在兩個(gè)線程同時(shí)訪問(wèn)同一個(gè)塊內(nèi)存區(qū)域的場(chǎng)景。
內(nèi)存的訪問(wèn)是通過(guò)一個(gè)一個(gè)地址總線串行排列訪問(wèn)的温自,所以在繼續(xù)后續(xù)之前玄货,我們先明確幾個(gè)結(jié)論:
1)內(nèi)存的訪問(wèn)是串行的,并不會(huì)導(dǎo)致多亂或者應(yīng)用的crash
2)bool悼泌,int松捉,long類型是原子性的。

多線程不安全的場(chǎng)景

@property (atomic, assign)    int       intA;
 
//thread A
for (int i = 0; i < 10000; i ++) {
    self.intA = self.intA + 1;
    NSLog(@"Thread A: %d\n", self.intA);
}
 
//thread B
for (int i = 0; i < 10000; i ++) {
    self.intA = self.intA + 1;
    NSLog(@"Thread B: %d\n", self.intA);
}

雖然intA聲明為原子的馆里,但是結(jié)果不一定是20000隘世,因?yàn)閟elf.intA = self.intA + 1;不是原子操作,雖然intA的setter和getter方法是原子操作鸠踪,但是語(yǔ)句不是原子的丙者,這行賦值代碼包括讀取load + 1(add),賦值(store)三部操作,當(dāng)線程A store的時(shí)候可能線程B已經(jīng)執(zhí)行了若干次store了,最后結(jié)果小于預(yù)期的值营密。

@property (atomic, strong) NSString*                 userName;
- (void)setUserName:(NSString *)userName {
    if(_uesrName != userName) {
        [userName retain];
        [_userName release];
        _userName = userName;
    }
}

不僅僅是賦值操作械媒,還會(huì)有retain,release調(diào)用卵贱。如果property為nonatomic滥沫,上述的setter方法就不是原子操作,我們可以假設(shè)一種場(chǎng)景键俱,線程1先通過(guò)getter獲取當(dāng)前_userName兰绣,之后線程2通過(guò)setter調(diào)用[_userName release];,線程1所持有的_userName就變成無(wú)效的地址空間了编振,如果再給這個(gè)地址空間發(fā)消息就會(huì)導(dǎo)致crash缀辩,出現(xiàn)多線程不安全的場(chǎng)景臭埋。

場(chǎng)景三
@property (atomic, strong) NSString*                 stringA;
 
//thread A
for (int i = 0; i < 100000; i ++) {
    if (i % 2 == 0) {
        self.stringA = @"a very long string";
    }
    else {
        self.stringA = @"string";
    }
    NSLog(@"Thread A: %@\n", self.stringA);
}
 
//thread B
for (int i = 0; i < 100000; i ++) {
    if (self.stringA.length >= 10) {
        NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
    }
    NSLog(@"Thread B: %@\n", self.stringA);
}

雖然stringA是atomic的property臀玄,而且在取substring的時(shí)候做了length判斷,線程B還是很容易crash健无,因?yàn)樵谇耙豢套xlength的時(shí)候self.stringA = @"a very long string";荣恐,下一刻取substring的時(shí)候線程A已經(jīng)將self.stringA = @"string";叠穆,立即出現(xiàn)out of bounds的Exception,crash臼膏,多線程不安全。

@property (atomic, strong) NSArray*                 arr;
 
//thread A
for (int i = 0; i < 100000; i ++) {
    if (i % 2 == 0) {
        self.arr = @[@"1", @"2", @"3"];
    }
    else {
        self.arr = @[@"1"];
    }
    NSLog(@"Thread A: %@\n", self.arr);
}
 
//thread B
for (int i = 0; i < 100000; i ++) {
    if (self.arr.count >= 2) {
        NSString* str = [self.arr objectAtIndex:1];
    }
    NSLog(@"Thread B: %@\n", self.arr);
}

同理渗磅,即使我們?cè)谠L問(wèn)objectAtIndex之前做了count的判斷,線程B依舊很容易crash始鱼,原因也是由于前后兩行代碼之間arr所指向的內(nèi)存區(qū)域被其他線程修改了。

總結(jié)

atomic的作用只是給getter和setter加了個(gè)鎖医清,atomic只能保證代碼進(jìn)入getter或者setter方法內(nèi)部時(shí)是安全的,一旦出了getter和setter状勤,多線程安全只能靠程序員自己保障了鞋怀。所以atomic屬性和使用property的多線程安全并沒(méi)什么直接的聯(lián)系持搜。
atomic會(huì)帶來(lái)一些性能損耗,所以一般用nonatomic葫盼,在需要做多線程安全的場(chǎng)景,自己去額外加鎖做同步贫导。

線程安全的實(shí)現(xiàn)方法

非原子性的:

if (self.stringA.length >= 10) {
    NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
}

加鎖:

//thread A
[_lock lock];
for (int i = 0; i < 100000; i ++) {
    if (i % 2 == 0) {
        self.stringA = @"a very long string";
    }
    else {
        self.stringA = @"string";
    }
    NSLog(@"Thread A: %@\n", self.stringA);
}
[_lock unlock];
 
//thread B
[_lock lock];
if (self.stringA.length >= 10) {
    NSString* subStr = [self.stringA substringWithRange:NSMakeRange(0, 10)];
}
[_lock unlock];

加鎖以后認(rèn)為是線程安全的。

加鎖方式:

  1. @synchronized(token)
  2. NSLock

加鎖和關(guān)鎖要在同一個(gè)線程執(zhí)行孩灯,要不會(huì)產(chǎn)生不可預(yù)知的問(wèn)題。
遞歸加鎖不要用這個(gè)峰档,因?yàn)檎{(diào)用這個(gè)lock 的方法兩次在同一個(gè)線程里面會(huì)永久的鎖住這個(gè)線程寨昙。
遞歸用NSRecursiveLock 去實(shí)現(xiàn)遞歸加鎖。

  1. dispatch_semapgore_t
  2. OSSpinLock

性能損耗由上到下依次減小舔哪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市捉蚤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌外里,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鳖链,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芙委,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)灌侣,“玉大人,你說(shuō)我怎么就攤上這事侧啼∨F猓” “怎么了痊乾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蛾魄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)湿滓,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任叽奥,我火速辦了婚禮,結(jié)果婚禮上朝氓,老公的妹妹穿的比我還像新娘恋日。我一直安慰自己,他們只是感情好岂膳,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著谈截,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涧偷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天喻鳄,我揣著相機(jī)與錄音,去河邊找鬼确封。 笑死,一個(gè)胖子當(dāng)著我的面吹牛爪喘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秉剑,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼侦鹏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起略水,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聚请,沒(méi)想到半個(gè)月后荠雕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體驶赏,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年盖文,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚯姆。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洒敏,死狀恐怖疙驾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情它碎,我是刑警寧澤函荣,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布傻挂,位于F島的核電站,受9級(jí)特大地震影響挖息,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜套腹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沉迹。 院中可真熱鬧害驹,春花似錦、人聲如沸宛官。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至亥揖,卻和暖如春珊擂,著一層夾襖步出監(jiān)牢的瞬間费变,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工挚歧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扛稽,地道東北人滑负。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓用含,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啄骇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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

  • 線程通信 在一個(gè)進(jìn)程中肠缔,線程往往不是孤立存在的,多個(gè)線程之間需要經(jīng)常進(jìn)行通信線程通信的體現(xiàn):1明未、一個(gè)線程需要傳遞數(shù)...
    大猿媛閱讀 513評(píng)論 0 1
  • 在軟件編程中,多線程是個(gè)繞不開(kāi)的話題佣蓉。多線程的使用,能夠提高程序的運(yùn)行效率勇凭,但也帶來(lái)新的問(wèn)題:如何保證線程安全疚膊? ...
    星___塵閱讀 1,410評(píng)論 0 2
  • 前言 iOS開(kāi)發(fā)中由于各種第三方庫(kù)的高度封裝备徐,對(duì)鎖的使用很少,剛好之前面試中被問(wèn)到的關(guān)于并發(fā)編程鎖的問(wèn)題璧函,都是一知...
    喵渣渣閱讀 3,690評(píng)論 0 33
  • 晚上九點(diǎn)半箩艺,一個(gè)陌生的號(hào)碼打過(guò)來(lái)宪萄,我猶豫著接通舅桩,卻沒(méi)有先說(shuō)話雨膨。 大概過(guò)了三秒鐘,電話那頭說(shuō):“喂,老師撒妈,你猜我...
    夏冬陽(yáng)閱讀 974評(píng)論 1 2
  • 百嬌千媚嫩嫩葉, 姹紫嫣紅美美花狰右。 春日無(wú)處不飛絮杰捂, 滿城盡帶白玉甲棋蚌。
    春暖花開(kāi)ta閱讀 323評(píng)論 0 1