iOS多線程學(xué)習(xí)二NSTread

NSThread是蘋果官方提供的,使用OC代碼編寫,使用起來(lái)比pthread更加面向?qū)ο螅?jiǎn)單易用又固,可以直接操作線程對(duì)象仲器,需要我們手動(dòng)管理線程的生命周期。NSThread是一個(gè)基于pthreads使用OC代碼封裝.

關(guān)于NSThread相關(guān)API我這里會(huì)結(jié)合對(duì)應(yīng)功能使用做些說(shuō)明仰冠。詳細(xì)的API說(shuō)明可參考官方文檔

NSThread的創(chuàng)建

使用NSThread該類創(chuàng)建線程有兩種方法:

  • 使用detachNewThreadSelector:toTarget:withObject:class方法生成新線程乏冀。
  • 創(chuàng)建一個(gè)新NSThread對(duì)象并調(diào)用其start方法。(僅在iOS和OS X v10.5及更高版本中受支持).

這兩個(gè)方法都會(huì)在應(yīng)用程序中創(chuàng)建一個(gè)分離的線程洋只。線程退出時(shí)系統(tǒng)會(huì)自動(dòng)回收線程的資源辆沦。

detachNewThreadSelector:toTarget:withObject:OS X的所有版本都支持該方法.提供要用作線程入口點(diǎn)的方法名稱(選擇器)昼捍,定義該方法的對(duì)象以及要在啟動(dòng)時(shí)傳遞給線程的任何數(shù)據(jù)。
使用實(shí)例:

 [NSThread detachNewThreadSelector:@selector(threadRun:) toTarget:self withObject:@"使用detachNewThreadSelector開(kāi)啟子線程"];
 - (void)threadRun:(NSString *)param {
    NSLog(@"----threadRun:%@ ----%@",[NSThread currentThread],param);
}

NSThreadOS Xv10.5的更高及版本中初始化對(duì)象的簡(jiǎn)單方法可使用initWithTarget:selector:object:方法肢扯。但是妒茬,它不會(huì)啟動(dòng)該線程。要啟動(dòng)該線程蔚晨,用start顯式調(diào)用線程對(duì)象的方法
使用該initWithTarget:selector:object:方法的

還可以子類繼承NSThread并覆蓋其main方法乍钻。用此方法的重寫實(shí)現(xiàn)線程的入口更多操作。

使用實(shí)例

//1.alloc init 創(chuàng)建線程铭腕,需要手動(dòng)啟動(dòng) 僅在iOS和OS X v10.5及更高版本中受支持
//創(chuàng)建一個(gè)新NSThread對(duì)象并調(diào)用其start方法银择,線程退出時(shí)系統(tǒng)會(huì)自動(dòng)回收線程的資源
- (void)createNSThreadA {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun:) object:@"使用initWithTarget開(kāi)啟NSThread子線程"];
    [thread start];
}

第三種 還可以使用線程通信的方式,使用performselector相關(guān)方法開(kāi)啟子線程
例如可以用performSelectorInBackground:withObject:開(kāi)啟后臺(tái)線程累舷。

使用實(shí)例

[self performSelectorInBackground:@selector(threadRun:) withObject:@"使用perform開(kāi)始子線程"];
NSthread 線程一些屬性說(shuō)明
配置線程堆棧大小

默認(rèn)情況下 子線程 堆棧大小512KB左右浩考,iOS主線程1M左右空間大小,
子線線程允許的最小堆棧大小為8 KB被盈,堆棧大小必須為4 KB的倍數(shù)析孽。
子線程堆棧大小我們可以通過(guò)代碼控制。
在iOS和OS X v10.5及更高版本中害捕,分配并初始化NSThread對(duì)象(不要使用該detachNewThreadSelector:toTarget:withObject:方法)绿淋。在調(diào)用start線程對(duì)象的方法之前使用setStackSize:方法指定新的堆棧大小。在線程啟動(dòng)后設(shè)置堆棧大小會(huì)更改屬性大小尝盼,但不會(huì)影響為線程預(yù)留的實(shí)際堆棧大小吞滞。
如果設(shè)置setStackSize:小于8KB或者不是4KB倍數(shù),系統(tǒng)都會(huì)拋出類似異常:
it must be a multiple of the system page size and greater than 8192

配置線程局部存儲(chǔ)鍵值對(duì)

每個(gè)線程都維護(hù)了一個(gè)鍵值對(duì)字典盾沫,可以再線程任何位置訪問(wèn)裁赠。可以使用NSTread對(duì)象屬性threadDictionary存儲(chǔ)要在整個(gè)線程執(zhí)行期間保留的信息赴精。

設(shè)置線程優(yōu)先級(jí)

使用NSThread的類方法setThreadPriority:;
傳入的參數(shù)是double類型需要在0.0~1.0范圍之間佩捞,默認(rèn)是0.5.線程優(yōu)先級(jí)越高,CPU調(diào)度的該線程的頻率會(huì)越高蕾哟。優(yōu)先級(jí)較高的線程比具有較低優(yōu)先級(jí)的線程更可能運(yùn)行一忱。較高優(yōu)先級(jí)并不能保證線程的特定執(zhí)行時(shí)間,只是與較低優(yōu)先級(jí)的線程相比谭确,調(diào)度程序更有可能選擇它帘营。

NSThread線程生命周期
相關(guān)函數(shù)

啟動(dòng)線程 進(jìn)入就緒->運(yùn)行狀態(tài)。任務(wù)執(zhí)行完畢自行銷毀

-(void)start

阻塞線程,進(jìn)入阻塞狀態(tài)

+ (void)sleepUntilDate:(NSDate *)date
+ (void)sleepForTimeInterval:(NSTimeInterval)ti

更改接收器的取消狀態(tài)以指示它應(yīng)該退出.

- (void)cancel

取消線程并不會(huì)馬上停止并退出線程逐哈,僅僅用作(線程是否需要退出)狀態(tài)記錄
然后通過(guò)調(diào)用@property(readonly, getter=isCancelled) BOOL cancelled獲取是否取消的狀態(tài)然后做相關(guān)操作芬迄。

終止當(dāng)前線程

+(void)exit

建議不要使用此方法。殺死一個(gè)線程可以防止該線程自行清理昂秃。線程分配的內(nèi)存可能會(huì)被泄露禀梳,并且線程當(dāng)前正在使用的任何其他資源可能無(wú)法正確清理杜窄,從而產(chǎn)生潛在問(wèn)題。
要在操作過(guò)程中終止線程算途,一開(kāi)始就設(shè)計(jì)線程以響應(yīng)取消或退出消息塞耕。調(diào)用- (void)cancel。再根@property(readonly, getter=isCancelled) BOOL cancelled狀態(tài)來(lái)是否調(diào)用+(void)exit郊艘。這樣線程將有機(jī)會(huì)執(zhí)行任何所需的清理并正常退出荷科。
還可以通過(guò)運(yùn)行循環(huán)輸入源來(lái)控制線程是否退出.這個(gè)涉及到runloop,后面我會(huì)研究纱注。有興趣可先看官方文檔說(shuō)明畏浆。

其實(shí)取消終止線程還有個(gè)快捷方法,如果使用detachNewThreadSelector:toTarget:withObject:class方法生成新線程狞贱】袒瘢可以直接使用類方法+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument快速取消一個(gè)線程。

線程安全和同步

如果多個(gè)線程訪問(wèn)統(tǒng)一資源,修改相同資源的兩個(gè)線程可能會(huì)以非預(yù)期的方式相互干擾瞎嬉。例如蝎毡,一個(gè)線程可能會(huì)覆蓋另一個(gè)線程的更改,或者將應(yīng)用程序置于未知且可能無(wú)效的狀態(tài)氧枣。這個(gè)時(shí)候我們就需要同步工具來(lái)是線程同步沐兵,確保它們?cè)诮换r(shí)安全地進(jìn)行交互。是多條線程按順序的訪問(wèn)同一塊資源便监。
線程同步有以下幾種方式:

原子操作

原子操作是一種簡(jiǎn)單的同步形式扎谎,適用于簡(jiǎn)單的數(shù)據(jù)類型。原子操作的優(yōu)點(diǎn)是它們不會(huì)阻止競(jìng)爭(zhēng)線程烧董。對(duì)于簡(jiǎn)單的操作毁靶,例如遞增計(jì)數(shù)器變量,這可以帶來(lái)比獲取鎖定更好的性能.可以對(duì)32位或64位值執(zhí)行簡(jiǎn)單的數(shù)學(xué)和邏輯運(yùn)算逊移。這些操作依賴于特殊的硬件指令,以確保在再次訪問(wèn)受影響的內(nèi)存之前完成給定的操作.使用需要導(dǎo)入頭文件<libkern/OSAtomic.h>.
相關(guān)API參考atomic预吆。
這里不做過(guò)多說(shuō)明。

鎖是iOS 最常用的同步工具之一胳泉。
iOS 鎖大概有以下幾種類型:

  • 互斥鎖
  • 遞歸鎖
  • 讀寫鎖(共享獨(dú)占鎖)
  • 分布式鎖
  • 自旋鎖
  • 雙重鎖

這里就不過(guò)多討論拐叉。后面我會(huì)研究鎖相關(guān)的東西。

感興趣的可以看看其他人的文章iOS開(kāi)發(fā)中的11種鎖以及性能對(duì)比

這里我就@synchronized這個(gè)互斥鎖做下簡(jiǎn)單說(shuō)明和使用實(shí)例

@synchronized是在Objective-C代碼中動(dòng)態(tài)創(chuàng)建互斥鎖的便捷方式
使用如下:

 @synchronized(Obj)
    {
        //大括號(hào)之間的所有內(nèi)容都受@synchronized指令保護(hù)扇商。
    }

傳遞給@synchronized指令的對(duì)象obj是用于區(qū)分受保護(hù)塊的唯一標(biāo)識(shí)符凤瘦。如果在兩個(gè)不同的線程中執(zhí)行上述方法,則obj在每個(gè)線程上為參數(shù)傳遞一個(gè)不同的對(duì)象钳吟,每個(gè)線程都會(huì)鎖定并繼續(xù)處理廷粒,而不會(huì)被另一個(gè)阻塞窘拯。但是红且,如果在兩種情況下都傳遞相同的對(duì)象坝茎,則其中一個(gè)線程將首先獲取鎖定,另一個(gè)線程將阻塞暇番,直到第一個(gè)線程完成鎖定的部分嗤放。

這里就用常用的一個(gè)例子購(gòu)買車票為例子說(shuō)明線程安全與同步問(wèn)題。

- (void)createNSThreadD {
   //全局變量總票數(shù)
    totalCount = 100;
    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(sellingTickets) object:nil];
    threadA.name = @"售票員A";
    [threadA start];
    NSThread * threadB = [[NSThread alloc] initWithTarget:self selector:@selector(sellingTickets) object:nil];
    threadB.name = @"售票員B";
    [threadB start];
    NSThread *threadC = [[NSThread alloc] initWithTarget:self selector:@selector(sellingTickets) object:nil];
    threadC.name = @"售票員C";
    [threadC start];
}

如果sellingTickets方法類不加鎖

- (void)sellingTickets {
    
    while (1) {
            NSInteger currentCount = totalCount;
            if (currentCount>0) {
                //模擬耗時(shí)操作
                for (NSInteger i = 0; i<1000000; i++) {
                    
                }
                
                totalCount = currentCount-1;
                NSLog(@"售票員%@售出一張票剩余%ld張票",[NSThread currentThread].name,totalCount);
                
            }else {
                NSLog(@"%@當(dāng)前票已經(jīng)售完",[NSThread currentThread].name);
                break;
            }
    }
}

會(huì)看到如下同一時(shí)刻不同售票員售出查詢余票有沖突結(jié)果:


不加鎖

使用了@synchronized鎖后完全正常

- (void)sellingTickets {
    
    while (1) {
       @synchronized (self) {
            NSInteger currentCount = totalCount;
            if (currentCount>0) {
                //模擬耗時(shí)操作
                for (NSInteger i = 0; i<1000000; i++) {
                    
                }
                
                totalCount = currentCount-1;
                NSLog(@"售票員%@售出一張票剩余%ld張票",[NSThread currentThread].name,totalCount);
                
            }else {
                NSLog(@"%@當(dāng)前票已經(jīng)售完",[NSThread currentThread].name);
                break;
            }
        }
        
    }
}
加鎖之后
使用條件

條件是另一種類型的信號(hào)量壁酬,它允許線程在某個(gè)條件為真時(shí)相互發(fā)信號(hào)次酌。條件通常用于指示資源的可用性或確保以特定順序執(zhí)行任務(wù)。這里先不做過(guò)多說(shuō)明舆乔。我的上一篇文章iOS多線程學(xué)習(xí)(一)pthread對(duì)條件作了簡(jiǎn)單說(shuō)明岳服。

NSObject執(zhí)行選擇器

這個(gè)我們最熟悉,使用NSobjectperformSelector相關(guān)函數(shù)希俩,用線程間通信實(shí)現(xiàn)實(shí)現(xiàn)線程間同步吊宋。

參考:
Threading Programming Guide
Concurrent Programming: APIs and Challenges
上一篇:

iOS多線程學(xué)習(xí)(一)pthread
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市颜武,隨后出現(xiàn)的幾起案子璃搜,更是在濱河造成了極大的恐慌,老刑警劉巖鳞上,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件这吻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡篙议,警方通過(guò)查閱死者的電腦和手機(jī)唾糯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涡上,“玉大人趾断,你說(shuō)我怎么就攤上這事》岳ⅲ” “怎么了芋酌?”我有些...
    開(kāi)封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)雁佳。 經(jīng)常有香客問(wèn)我脐帝,道長(zhǎng),這世上最難降的妖魔是什么糖权? 我笑而不...
    開(kāi)封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任堵腹,我火速辦了婚禮,結(jié)果婚禮上星澳,老公的妹妹穿的比我還像新娘疚顷。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布腿堤。 她就那樣靜靜地躺著阀坏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笆檀。 梳的紋絲不亂的頭發(fā)上忌堂,一...
    開(kāi)封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音酗洒,去河邊找鬼士修。 笑死,一個(gè)胖子當(dāng)著我的面吹牛樱衷,可吹牛的內(nèi)容都是我干的棋嘲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼矩桂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼封字!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起耍鬓,我...
    開(kāi)封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤阔籽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后牲蜀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笆制,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年涣达,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了在辆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡度苔,死狀恐怖匆篓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寇窑,我是刑警寧澤鸦概,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站甩骏,受9級(jí)特大地震影響窗市,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饮笛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一咨察、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧福青,春花似錦摄狱、人聲如沸脓诡。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)誉券。三九已至,卻和暖如春刊愚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踩验。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工鸥诽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箕憾。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓牡借,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親袭异。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钠龙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355