iOS多線程學習筆記

多線程種類

說句無關緊要的話,終于會用簡書的樣式引用了~~~本文借鑒大神的講解,鏈接在此:http://www.lxweimin.com/p/0b0d9b1f1f19

目前多線程分為四種:

Pthreads

NSThread

GCD

NSOperation&NSOperationQueue

Pthreads

一套在很多操作系統上都通用的多線程API,移植性很強,當然在 iOS 中也是可以的。是基于c語言的框架。

用法:

#import ?<pthread.h>

代碼用法:

pthread_t thread;

//創建一個線程并自動執行

pthread_create(&thread, NULL, start, NULL);

void *start(void *data) {

NSLog(@"%@", [NSThread currentThread]);

return NULL;

}

:需要手動處理線程的各個狀態的轉換即管理生命周期,比如,這段代碼雖然創建了一個線程,但并沒有銷毀。

NSThread

經過蘋果封裝,但是仍然需要手動管理內存。

代碼用法:(手動啟動)

// 創建

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

// 啟動

[thread start];

(自動啟動)

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];

除了創建啟動以外的其他方法:

//取消線程

- (void)cancel;

//啟動線程

- (void)start;

//判斷某個線程的狀態的屬性

@property (readonly, getter=isExecuting) BOOL executing;

@property (readonly, getter=isFinished) BOOL finished;

@property (readonly, getter=isCancelled) BOOL cancelled;

//設置和獲取線程名字

-(void)setName:(NSString *)name;

-(NSString *)name;

//獲取當前線程信息

+ (NSThread *)currentThread;

//獲取主線程信息

+ (NSThread *)mainThread;

//使當前線程暫停一段時間,或者暫停到某個時刻

+ (void)sleepForTimeInterval:(NSTimeInterval)time;

+ (void)sleepUntilDate:(NSDate *)date;

GCD

Grand Central Dispatch,它是蘋果為多核的并行運算提出的解決方案,所以會自動合理地利用更多的CPU內核(比如雙核、四核),最重要的是它會自動管理線程的生命周期(創建線程、調度任務、銷毀線程),完全不需要我們管理,我們只需要告訴干什么就行。同時它使用的也是c語言,不過由于使用了 Block(Swift里叫做閉包),使得使用起來更加方便,而且靈活。

任務和隊列

在GCD中,加入了兩個非常重要的概念:任務隊列

1)任務:即操作,你想要干什么,說白了就是一段代碼,在 GCD 中就是一個 Block,所以添加任務十分方便。任務有兩種執行方式:同步執行異步執行,他們之間的區別是是否會創建新的線程。

同步執行:在當前的線程執行,不會開啟新線程,它會阻塞當前線程并等待Block中的任務執行完畢,然后當前線程才會繼續往下運行。

異步執行:會開啟新線程執行,不會阻塞當前線程

2)隊列:用于存放任務。一共有兩種隊列,串行隊列并行隊列

串行隊列中的任務,GCD 會FIFO(先進先出)地取出來一個,執行一個,然后取下一個,這樣一個一個的執行。

并行隊列的任務,GCD 也會FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。這樣由于取的動作很快,忽略不計,看起來,所有的任務都是一起執行的。不過需要注意,GCD 會根據系統資源控制并行的數量,所以如果任務很多,它并不會讓所有任務同時執行。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 同步執行 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?異步執行

串行隊列 ? ? ? ? ? ? 當前線程,一個一個執行 ? ? ? ? ? ? ? ? ? ? ? ? ?其他線程,一個一個執行

并行隊列 ? ? ? ? ? ? 當前線程,一個一個執行 ? ? ? ? ? ? ? ? ? ? ? ? ?其他線程,一起執行

3)創建隊列

>主隊列:這是一個特殊的串行隊列。它用于刷新 UI,任何需要刷新 UI 的工作都要在主隊列執行,所以一般耗時的任務都要放到別的線程執行。

dispatch_queue_t queue = ispatch_get_main_queue();

>自己創建的隊列:自己可以創建串行隊列, 也可以創建并行隊列。第二個參數用來表示創建的隊列是串行的還是并行的,傳入DISPATCH_QUEUE_SERIAL或NULL表示創建串行隊列。傳入DISPATCH_QUEUE_CONCURRENT表示創建并行隊列

//串行隊列

dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);

dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);

//并行隊列

dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);

>全局并行隊列:只要是并行任務一般都加入到這個隊列。這是系統提供的一個并發隊列。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

4)創建任務

同步任務:會阻塞當前線程 (SYNC)

dispatch_sync(<#queue#>, ^{

//code here

NSLog(@"%@", [NSThread currentThread]);

});

異步任務:不會阻塞當前線程(ASYNC)

dispatch_async(<#queue#>, ^{

//code here

NSLog(@"%@", [NSThread currentThread]);

});

例子一:

NSLog("之前 - %@",NSThread.currentThread())

dispatch_sync(dispatch_get_main_queue(), { () ->Void in

NSLog("sync - %@",NSThread.currentThread())})

NSLog("之后 - %@",NSThread.currentThread())

解析:

同步任務會阻塞當前線程,然后把 Block 中的任務放到指定的隊列中執行,只有等到 Block 中的任務完成后才會讓當前線程繼續往下運行。

那么這里的步驟就是:打印完第一句后,dispatch_sync立即阻塞當前的主線程,然后把 Block 中的任務放到main_queue中,可是main_queue中的任務會被取出來放到主線程中執行,但主線程這個時候已經被阻塞了,所以 Block 中的任務就不能完成,它不完成,dispatch_sync就會一直阻塞主線程,這就是死鎖現象。導致主線程一直卡死。

例子二:

let queue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_SERIAL)

NSLog("之前 - %@",NSThread.currentThread())? ??

dispatch_async(queue, { () ->Void in

NSLog("sync之前 - %@",NSThread.currentThread())? ? ? ??

dispatch_sync(queue, { () ->Void in

NSLog("sync - %@",NSThread.currentThread())? ? ? ? })

NSLog("sync之后 - %@",NSThread.currentThread())? })

NSLog("之后 - %@",NSThread.currentThread())

解析:

使用DISPATCH_QUEUE_SERIAL這個參數,創建了一個串行隊列

打印出之前 - %@這句。

dispatch_async異步執行,所以當前線程不會被阻塞,于是有了兩條線程,一條當前線程繼續往下打印出之后 - %@這句, 另一臺執行 Block 中的內容打印sync之前 - %@這句。因為這兩條是并行的,所以打印的先后順序無所謂。

注意,高潮來了。現在的情況和上一個例子一樣了。dispatch_sync同步執行,于是它所在的線程會被阻塞,一直等到sync里的任務執行完才會繼續往下。于是sync就高興的把自己 Block 中的任務放到queue中,可誰想queue是一個串行隊列,一次執行一個任務,所以sync的 Block 必須等到前一個任務執行完畢,可萬萬沒想到的是queue正在執行的任務就是被sync阻塞了的那個。于是又發生了死鎖。所以sync所在的線程被卡死了。剩下的兩句代碼自然不會打印。

隊列組

隊列組可以將很多隊列添加到一個組里,這樣做的好處是,當這個組里所有的任務都執行完了,隊列組會通過一個方法通知我們。

//1.創建隊列組

dispatch_group_t group = dispatch_group_create();

//2.創建隊列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//3.多次使用隊列組的方法執行任務, 只有異步方法

//3.1.執行3次循環

dispatch_group_async(group, queue, ^{

for (NSInteger i = 0; i < 3; i++) {

NSLog(@"group-01 - %@", [NSThread currentThread]);

}

});

//3.2.主隊列執行8次循環

dispatch_group_async(group, dispatch_get_main_queue(), ^{

for (NSInteger i = 0; i < 8; i++) {

NSLog(@"group-02 - %@", [NSThread currentThread]);

}

});

//3.3.執行5次循環

dispatch_group_async(group, queue, ^{

for (NSInteger i = 0; i < 5; i++) {

NSLog(@"group-03 - %@", [NSThread currentThread]);

}

});

//4.都完成后會自動通知

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

NSLog(@"完成 - %@", [NSThread currentThread]);

});

NSOperation&NSOperationQueue

NSOperation 是蘋果公司對 GCD 的封裝,完全面向對象,所以使用起來更好理解。 大家可以看到NSOperation NSOperationQueue分別對應 GCD 的任務隊列。操作步驟也很好理解:

將要執行的任務封裝到一個NSOperation對象中。

將此任務添加到一個NSOperationQueue對象中。

1)添加任務

NSOperation只是一個抽象類,所以不能封裝任務。但它有 2 個子類用于封裝任務。分別是:NSInvocationOperationNSBlockOperation。創建一個 Operation 后,需要調用start方法來啟動任務,它會默認在當前隊列同步執行。當然你也可以在中途取消一個任務,只需要調用其cancel方法即可。

>NSInvocationOperation : 需要傳入一個方法名。

//1.創建NSInvocationOperation對象

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

//2.開始執行

[operation start];

>NSBlockOperation:

//1.創建NSBlockOperation對象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//2.開始任務

[operation start];

NSBlockOperation還有一個方法:addExecutionBlock:,通過這個方法可以給 Operation 添加多個執行 Block。這樣 Operation 中的任務會并發執行,它會在主線程和其它的多個線程執行這些任務。

//1.創建NSBlockOperation對象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//添加多個Block

for (NSInteger i = 0; i < 5; i++) {

[operation addExecutionBlock:^{

NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

}];

}

//2.開始任務

[operation start];

注:addExecutionBlock方法必須在start()方法之前執行!

2)創建隊列

>主隊列

NSOperationQueue *queue = [NSOperationQueue mainQueue];

>其他隊列

因為主隊列比較特殊,所以會單獨有一個類方法來獲得主隊列。那么通過初始化產生的隊列就是其他隊列。

//1.創建一個其他隊列

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//2.創建NSBlockOperation對象

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"%@", [NSThread currentThread]);

}];

//3.添加多個Block

for (NSInteger i = 0; i < 5; i++) {

[operation addExecutionBlock:^{

NSLog(@"第%ld次:%@", i, [NSThread currentThread]);

}];

}

//4.隊列添加任務

[queue addOperation:operation];

>隊列添加依賴

//1.任務一:下載圖片

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"下載圖片 - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//2.任務二:打水印

NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"打水印? - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//3.任務三:上傳圖片

NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"上傳圖片 - %@", [NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//4.設置依賴

[operation2 addDependency:operation1];? ? ? //任務二依賴任務一

[operation3 addDependency:operation2];? ? ? //任務三依賴任務二

//5.創建隊列并加入任務

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

注:不能添加相互依賴,會死鎖,比如 A依賴B,B依賴A。

可以使用removeDependency來解除依賴關系。

可以在不同的隊列之間依賴,反正就是這個依賴是添加到任務身上的,和隊列沒關系。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容