溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

發(fā)布時間:2020-06-30 06:25:07 來源:網(wǎng)絡(luò) 閱讀:1755 作者:iKingLai 欄目:移動開發(fā)

線程的風(fēng)險


當(dāng)運(yùn)行在一個多線程環(huán)境中,你總是需要注意一些事情:你不能控制線程執(zhí)行的順序。例如,如果你有兩個線程,線程1和線程2,CPU可能會在線程1上運(yùn)行一段時間,然后又會切到線程2運(yùn)行一段時間。問題是你不知道CPU何時切過去,也不知道會為一個線程分配多少時間。每個線程運(yùn)行的時間都不是公平的。


為了演示線程不容易控制帶來的風(fēng)險,我會舉一個例子。這個例子包括兩個線程:線程1和線程2. 線程1打印奇數(shù),線程2打印偶數(shù)。這些數(shù)的范圍從1到20. 線程1先啟動,然后線程2再啟動。這個例子將會運(yùn)行3次。Listing 6-5 顯示了樣例代碼。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六


正如你看到的,我先調(diào)用打印奇數(shù),然后再打印偶數(shù)。你可能會期待看到

  • 先打印一些奇數(shù)

  • 奇數(shù)和偶數(shù)平均打印,例如兩個奇數(shù)和兩個偶數(shù)


但是,這些猜測都是不正確的,你可以看表格 6-4, 顯示了3次運(yùn)行的結(jié)果。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六


你看下第2次,0先打印出來,而其他的都是先打印1。奇數(shù)和偶數(shù)打印也不是平均的;而且,也沒什么跡象表明有多少奇數(shù)在偶數(shù)之前打印。


因此,在多線程環(huán)境中,你不能控制線程執(zhí)行的順序。多線程是一把雙刃劍。開發(fā)者實(shí)現(xiàn)一個多線程應(yīng)用需要注意下面的3個風(fēng)險。

  • 安全性:這個標(biāo)準(zhǔn)意味著在多線程環(huán)境下,輸出要跟預(yù)期的一致。換句話說,程序可以運(yùn)行在不同的順序中多次,但是最終的輸出必須是可預(yù)見的,正確的?!霸愀獾氖虑椴粫l(fā)生”。

  • 活躍性:這個和安全性不同。一種定義是“一些好的情況最終會發(fā)生”。例如,假設(shè)線程A必須等到線程B的結(jié)果,有時這些結(jié)果從來都不返回。因此,線程A從來不會計算最終結(jié)果。這個通常稱為死鎖。

  • 性能:iPhone應(yīng)用最重要的一個目標(biāo)就是有一個比較好的性能和更靈敏的UI。因此,你的性能目標(biāo)必須達(dá)到?;钴S性只關(guān)注一些最終發(fā)生的事情;它并不關(guān)心多快獲取到結(jié)果。


我將會在接下來的部分使用例子來涉及到每一個標(biāo)準(zhǔn),這樣你就可以理解什么樣會導(dǎo)致一個不好的結(jié)果,你如何解決它,使得你的應(yīng)用運(yùn)行時有一個比較高的性能。


安全性

安全性要求程序運(yùn)行在多線程環(huán)境中,產(chǎn)生一個正確的期待的結(jié)果,就想他運(yùn)行在單線程環(huán)境中一樣。我會討論一個潛在的在多線程環(huán)境中經(jīng)常會發(fā)生的一個問題,當(dāng)兩個或多個線程同時訪問相同的數(shù)據(jù)。


圖 6-5 描述了兩個線程如何返回一個相同的item而導(dǎo)致應(yīng)用崩潰。在圖6-5中,線程1嘗試把item push到當(dāng)前棧中。然后,線程2和線程3想要把item取出來,然后檢查確保這個item在這個棧中。但是,在兩個線程檢查之后,線程2先運(yùn)行,然后獲取item。Oops!像你看到的,線程3已經(jīng)沒有item可以獲取了。這會導(dǎo)致你的應(yīng)用崩潰。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

你可以從ThreadSafety工程中獲取到樣例代碼,但是Listing 6-6 顯示了這個問題的代碼注解。注意這個問題不會總是出現(xiàn),但是如果你運(yùn)行足夠多的時間,它還是會發(fā)生的。代碼使用了NSMutableArray變量存儲,因此客戶端代碼能夠添加和刪除數(shù)據(jù)。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六


當(dāng)你運(yùn)行上面的代碼一段時間,你會收到下面的信息:

iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

它告訴你,你嘗試在一個空的數(shù)組中刪除對象,這是不應(yīng)該發(fā)生的當(dāng)你在刪除之前已經(jīng)檢查了數(shù)組空的情況。你甚至打印出來看它是否是最后一個對象。


現(xiàn)在,如果你再一次查看圖6-4,你應(yīng)該理解為什么會崩潰 -- 因?yàn)榈谝粋€現(xiàn)場檢查和打印出最后一個對象后,第一個線程已經(jīng)停止,而第二個線程還在運(yùn)行。


解決辦法


對于這個問題我的解決辦法是鎖住這個方法直到線程執(zhí)行完。鎖是一種機(jī)制,它能夠確保在一個時間內(nèi)只有一個線程訪問一個指定的代碼塊。想象一下,你現(xiàn)在在一個比賽中,需要直接和很多人競爭。你和你的競爭者被問了一個問題,而誰先響鈴誰就能先回答問題。在第一個結(jié)束之后,另外一個人又可以搖鈴了。當(dāng)?shù)谝粋€人在回答問題時,這個鈴是被鎖住的。線程也是一樣的。你可以創(chuàng)建一個鎖,就像是你的鈴一樣:第一個得到鎖的線程(類似搖鈴)會阻塞其他所有的線程直到它結(jié)束。在第一個線程結(jié)束之后,鎖就開了(類似于其他人可以搖鈴了);其他線程能嘗試獲取鎖,而這個過程可以重復(fù)下去。


鎖機(jī)制的基本概念就是確保當(dāng)一個線程在執(zhí)行任務(wù)時,其他線程不能打斷。例如,如果線程1獲取到這個對象,然后打印出來,線程2必須等到線程1執(zhí)行完才能獲取和從數(shù)組中刪除對象。


這個鎖創(chuàng)建在一個對象上。如果線程1從對象A獲取了鎖,其他線程就不能再獲取這個對象的鎖了,這些線程必須等待線程1執(zhí)行完后,然后把鎖返回給對象A。


最簡單的方式獲取對象A的鎖的就是使用@synchronized(objA),如下面Listing 6-7 的代碼。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六


注意:在很多情況下,使用self作為鎖,效果是一樣的。你只需要確保你想要鎖住的對象使用同一個對象鎖即可。例如,你有兩個存儲變量,你可以考慮為每一個單獨(dú)是有關(guān)一個鎖。



圖6-6 顯示了@synchronized在線程中是如何工作的。


iOS使用多線程提高數(shù)據(jù)并發(fā)訪問 之六

你需要同時同步push和pop data這兩個方法,因?yàn)槿绻阒绘i住其中的一個方法,當(dāng)你pop檢查時,依然會存在風(fēng)險,還有存儲器push了很多數(shù)據(jù),而你不沒有按照你想要的方式獲取到對象。為了防止這些,你需要使用lockObj同時鎖住他們,這樣在一個時間段就只有一個方法在運(yùn)行。


你的代碼是安全的,但是依然還有兩個更重要的多線程屬性需要討論。



向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI