多線程 之 多線程的安全隱患

1亩钟、多線程的安全隱患原因

主要是因?yàn)橘Y源共享。當(dāng)一塊資源可能會(huì)被多個(gè)線程共享時(shí),也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源(比如多個(gè)線程訪問(wèn)同一個(gè)對(duì)象话速、同一個(gè)變量讶踪、同一個(gè)文件),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問(wèn)題泊交。

2乳讥、解決方案

使用線程同步技術(shù)(同步,就是協(xié)同步調(diào)廓俭,按預(yù)定的先后次序進(jìn)行)
常見的線程同步技術(shù)是:加鎖(加完鎖之后記得要解鎖)
gcd的串行隊(duì)列也可以實(shí)現(xiàn)線程同步

以下都是可以保證線程安全的線程同步方案:
OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

3云石、各種鎖的說(shuō)明

OSSpinLock
OSSpinLockLock是一種自旋鎖(高級(jí)鎖):在等待過(guò)程中不會(huì)睡眠,而是一直處于循環(huán)狀態(tài)研乒、忙等狀態(tài)汹忠,等待過(guò)程中會(huì)占用cpu資源。

不過(guò),現(xiàn)在OSSpinLockLock已經(jīng)不推薦使用了(iOS10廢棄)宽菜。
原因:可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題谣膳。即:如果等待鎖的線程優(yōu)先級(jí)較高,它會(huì)一直占用著CPU資源铅乡,優(yōu)先級(jí)低的線程就無(wú)法釋放鎖继谚。
(假如有兩個(gè)線程執(zhí)行任務(wù),線程1優(yōu)先級(jí)高阵幸,線程2優(yōu)先級(jí)低花履,當(dāng)線程2先執(zhí)行任務(wù)時(shí),OSSpinLock加鎖挚赊,cpu分配更多的資源執(zhí)行線程1诡壁,導(dǎo)致線程1一直加鎖失敗,線程2一直無(wú)法解鎖咬腕。)

//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//嘗試加鎖(如果需要等待就不加鎖欢峰,直接返回false;如果不需要等待就加鎖涨共,返回ture)
bool result = OSSpinLockTry(&lock);
//加鎖
OSSpinLock(&lock);
//解鎖
OSSpinLockUnlock(&lock);

os_unfair_lock
os_unfair_lock用于取代不安全的OSSpinLock纽帖。
os_unfair_lock是一種低級(jí)鎖:在等待過(guò)程中會(huì)睡眠,這樣的鎖在喚起時(shí)候也會(huì)浪費(fèi)很多的資源举反。

//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//嘗試加鎖,同OSSpinLock
bool result = os_unfair_lock_trylock(&lock);
//加鎖
os_unfair_lock_lock(&lock);
//解鎖
os_unfair_lock_unlock(&lock);

pthread_mutex

  • pthread_mutex是一個(gè)互斥鎖懊直。
    當(dāng)設(shè)置屬性為PTHREAD_MUTEX_NORMAL時(shí)候,鎖就是默認(rèn)default為互斥鎖火鼻。
//初始化鎖的屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
//初始化鎖
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
//嘗試加鎖
pthread_mutex_trylock(&mutex);
//加鎖
pthread_mutex_lock(&mutex);
//解鎖
pthread_mutex_unlock(&mutex);
//銷魂相關(guān)屬性
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);
  • pthread_mutex:遞歸
    當(dāng)設(shè)置屬性為PTHREAD_MUTEX_RECURSIVE室囊,為遞歸鎖(屬于互斥鎖的一種特例),允許同一個(gè)線程在未釋放其擁有的鎖時(shí)反復(fù)對(duì)該鎖進(jìn)行加鎖操作魁索。
    // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//修改屬性為遞歸鎖
    // 初始化鎖
    pthread_mutex_init(mutex, &attr);
    //銷毀屬性
    pthread_mutexattr_destroy(&attr);
    //銷毀鎖
    pthread_mutex_destroy(mutex);
  • pthread_mutex:條件
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
//初始化鎖
pthread_mutex_init(&_mutex, &attr);
//銷毀屬性
pthread_mutexattr_destroy(&attr);
//初始化條件
pthread_cond_t _cond;
pthread_cond_init(&_cond, NULL);
//等待條件(進(jìn)入休眠融撞,放開mutex鎖,被喚醒后粗蔚,會(huì)再次對(duì)mutex加鎖)
pthread_cond_wait(&_cond, &_mutex);
//激活一個(gè)等待該條件的線程
pthread_cond_signal(&_cond);
//激活所有等待該條件的線程
pthread_cond_broadcast(&_cond);
//銷毀資源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);

NSLock尝偎、NSRecursiveLock、NSCondition
NSLock是對(duì)mutex普通鎖的封裝
NSRecursiveLock也是對(duì)mutex遞歸鎖的封裝
NSCondition是對(duì)mutex和cond的封裝

//NSLock的API
NSLock *lock = [[NSLock alloc] init];//初始化鎖
[lock lock];//上鎖
[lock unlock];//解鎖


//NSRecursiveLock的API
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];//初始化鎖
[recursiveLock lock];//上鎖
[recursiveLock unlock];//解鎖


//NSCondition的API
self.condition = [[NSCondition alloc] init];//初始化
[self.condition wait];//等待條件
[self.condition signal];//激活一個(gè)等待該條件的線程
[self.condition broadcast]; //激活所有等待該條件的線程
[self.condition unlock];//解鎖

NSConditionLock
NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝鹏控,可以設(shè)置具體的條件值

//初始化
- (instancetype)initWithCondition:(NSInteger)condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;

dispatch_queue (串行serial)
直接使用GCD的串行隊(duì)列致扯,也是可以實(shí)現(xiàn)線程同步的
因?yàn)镚CD的串行隊(duì)列訪問(wèn)同一資源的時(shí)候,本質(zhì)就是一個(gè)一個(gè)按照順序去執(zhí)行的

dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
        
});

dispatch_semaphore
semaphore叫做”信號(hào)量”
信號(hào)量的初始值当辐,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量
信號(hào)量的初始值為1抖僵,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步

//初始化信號(hào)量
dispatch_semaphore_t  semaphore = dispatch_semaphore_create(long value);
// 如果信號(hào)量的值 > 0缘揪,就讓信號(hào)量的值減1耍群,然后繼續(xù)往下執(zhí)行代碼
// 如果信號(hào)量的值 <= 0义桂,就會(huì)休眠等待,直到信號(hào)量的值變成>0世吨,就讓信號(hào)量的值減1澡刹,然后繼續(xù)往下執(zhí)行代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//讓信號(hào)量的值+1
dispatch_semaphore_signal(semaphore);

@synchronized
@synchronized是對(duì)mutex遞歸鎖的封裝
@synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖,然后進(jìn)行加鎖耘婚、解鎖操作

@synchronized (self) {
     //任務(wù)
}

以上鎖的使用實(shí)例子見github:https://github.com/ychen3022/lockDemo.git

4罢浇、總結(jié)
  • <1>以上各種鎖的性能怎么樣?
os_unfair_lock   //性能最好沐祷,但是iOS10才能用嚷闭,是對(duì)os_unfair_lock的替代
OSSpinLock  //自旋鎖,不建議使用赖临,已經(jīng)廢棄了
dispatch_semaphore  //性能好胞锰,而且ios8、9都可以使用
pthread_mutex //互斥鎖兢榨,推薦使用
dispatch_queue(DISPATCH_QUEUE_SERIAL)//gcd的串行隊(duì)列
NSLock  //互斥鎖嗅榕,是對(duì)mutex的封裝
NSCondition//互斥鎖
pthread_mutex(recursive) //遞歸鎖,相比mutext普通鎖吵聪,要耗性能一些
NSRecursiveLock//互斥鎖
NSConditionLock//對(duì)NSCondition的進(jìn)一步封裝凌那,互斥鎖
@synchronized//性能最差,是NSLock的一種封裝吟逝,犧牲了效率帽蝶,簡(jiǎn)潔了語(yǔ)法。
  • <2>自旋鎖和互斥鎖的對(duì)比块攒?
    (1)励稳、自旋鎖:稱之為高級(jí)鎖,會(huì)忙等囱井,不休眠驹尼。即在訪問(wèn)被鎖資源時(shí),調(diào)用者線程不會(huì)休眠庞呕,而是不停循環(huán)在那里新翎,直到被鎖資源釋放鎖。
    (2)千扶、互斥鎖:稱之為低級(jí)鎖料祠,會(huì)休眠骆捧。即在訪問(wèn)被鎖資源時(shí)澎羞,調(diào)用者線程會(huì)休眠,此時(shí)cpu可以調(diào)度其他線程工作敛苇。直到被鎖資源釋放鎖妆绞。此時(shí)會(huì)喚醒休眠線程顺呕。

  • 什么情況使用自旋鎖比較劃算?
    預(yù)計(jì)線程等待鎖的時(shí)間很短
    加鎖的代碼(臨界區(qū))經(jīng)常被調(diào)用括饶,但競(jìng)爭(zhēng)情況很少發(fā)生
    CPU資源不緊張
    多核處理器

  • 什么情況使用互斥鎖比較劃算株茶?
    預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
    單核處理器
    臨界區(qū)有IO(文件讀寫)操作,因?yàn)镮O操作都耗資源
    臨界區(qū)代碼復(fù)雜或者循環(huán)量大
    臨界區(qū)競(jìng)爭(zhēng)非常激烈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末图焰,一起剝皮案震驚了整個(gè)濱河市启盛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌技羔,老刑警劉巖僵闯,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異藤滥,居然都是意外死亡鳖粟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門拙绊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)向图,“玉大人,你說(shuō)我怎么就攤上這事标沪¢剩” “怎么了?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵谨娜,是天一觀的道長(zhǎng)航攒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)趴梢,這世上最難降的妖魔是什么漠畜? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮坞靶,結(jié)果婚禮上憔狞,老公的妹妹穿的比我還像新娘。我一直安慰自己彰阴,他們只是感情好瘾敢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尿这,像睡著了一般簇抵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上射众,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天碟摆,我揣著相機(jī)與錄音,去河邊找鬼叨橱。 笑死典蜕,一個(gè)胖子當(dāng)著我的面吹牛断盛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愉舔,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼钢猛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了轩缤?” 一聲冷哼從身側(cè)響起命迈,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎火的,沒想到半個(gè)月后躺翻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卫玖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年公你,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片假瞬。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陕靠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脱茉,到底是詐尸還是另有隱情剪芥,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布琴许,位于F島的核電站税肪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏榜田。R本人自食惡果不足惜益兄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望箭券。 院中可真熱鬧净捅,春花似錦、人聲如沸辩块。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)废亭。三九已至国章,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豆村,已是汗流浹背液兽。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留你画,地道東北人抵碟。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像坏匪,于是被迫代替她去往敵國(guó)和親拟逮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

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

  • 一 多線程的安全隱患 資源共享1塊資源可能會(huì)被多個(gè)線程共享适滓,也就是多個(gè)線程可能會(huì)訪問(wèn)同一塊資源比如多個(gè)線程訪問(wèn)同一...
    路飛_Luck閱讀 1,307評(píng)論 0 7
  • 一敦迄、簡(jiǎn)介:多線程在之前進(jìn)行過(guò)一篇詳細(xì)的基礎(chǔ)博客 iOS多線程 二、多線程的基礎(chǔ)知識(shí)回顧 1.1凭迹、iOS中的常見多線...
    IIronMan閱讀 888評(píng)論 0 4
  • Q:為什么出現(xiàn)多線程罚屋? A:為了實(shí)現(xiàn)同時(shí)干多件事的需求(并發(fā)),同時(shí)進(jìn)行著下載和頁(yè)面UI刷新嗅绸。對(duì)于處理器脾猛,為每個(gè)線...
    幸福相依閱讀 1,576評(píng)論 0 2
  • 目錄 1、為什么要線程安全 2鱼鸠、自旋鎖和互斥鎖 3猛拴、鎖的類型1、OSSpinLock2蚀狰、os_unfair_loc...
    SunshineBrother閱讀 1,163評(píng)論 0 20
  • 本文節(jié)選自成長(zhǎng)手冊(cè) 文章推薦和參考深入理解 iOS 開發(fā)中的鎖pthread的各種同步機(jī)制 多線程編程被普遍認(rèn)為復(fù)...
    百草紀(jì)閱讀 2,798評(píng)論 1 9