2018年4月30日 星期一

NSThread, GCD

NSThread

有三種方式可以開Thread
  1. NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:nil];
  2. [NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"分离子线程"];
    [self performSelectorInBackground:@selector(test:) withObject:@"后台线程"];

第一種方式可以取得該線程的instance,也可以設定Thread Priority,但要手動啟動線程
第二三種方式直接開啟線程,但無法取得instance
performSelector是比較早期的API,跟NSThreadㄧ樣呼叫一次就開一個線程 
目前Object-C大多使用GCD或是NSOperationQueue,這兩個API會自行管理線程數量



GCD

分作兩種模式
  1. dispatch_async :非同步
  2. dispatch_sync:同步
同步模式下會先等待Block裡面的動作執行完,才繼續往下跑
個人感覺有點類似C#的async Task 搭配 await語法,會把工作丟給別的Thread去做,但會等工作做完才繼續動作

實際上使用dispatch_sync後,發現動作並沒有在其他Thread執行,而是使用main thread,爬文之後發現有人在討論這個問題



也就是說因為main queue是serial queue,呼叫dispatch_sync必定導致線程block住,無所謂執行block內工作的線程,所以程式會直接使用main queue執行,節省開線程的資源

如果今天是在背景呼叫dispatch_sync把工作丟給main queue,就會有線程切換的動作

簡單來說 dispatch_sync 不會開新線程,但在特定情況下,會有線程切換

另外使用dispatch_async也不一定呼叫幾次就開啟多少線程,程式會自行控制
推測應該跟C#一樣使用thread pool的概念
有文章說線程上限66個,serial 與 concurrent共用這數量


使用GCD要小心Block的問題,千萬不要在同步模式(dispatch_sync) 下丟一個Block給自己的線程,自己等自己會卡住

dispatch_queue 可以分為兩種,serial queue與concurrent queue
main_queue:serial queue
global_queue:於concurrent queue
用dispatch_queue_t可以自行宣告,自行決定類型

GCD使用方法



//sync呼叫 會在main queue執行
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(int i=0;i<5;i++){
NSLog(@"sync: %d %@",i, [NSThread currentThread]);
}
});
//2018-04-30 16:42:57.905789+0800 NSTimer_NSRunLoop[4045:160977] sync: 0 <NSThread: 0x604000065540>{number = 1, name = main}
//2018-04-30 16:42:57.905875+0800 NSTimer_NSRunLoop[4045:160977] sync: 1 <NSThread: 0x604000065540>{number = 1, name = main}
//2018-04-30 16:42:57.905904+0800 NSTimer_NSRunLoop[4045:160977] sync: 2 <NSThread: 0x604000065540>{number = 1, name = main}
//2018-04-30 16:42:57.905929+0800 NSTimer_NSRunLoop[4045:160977] sync: 3 <NSThread: 0x604000065540>{number = 1, name = main}
//2018-04-30 16:42:57.905951+0800 NSTimer_NSRunLoop[4045:160977] sync: 4 <NSThread: 0x604000065540>{number = 1, name = main}
sleep(1);
//async global queue
//global queue都是concurrent,所以印出來的結果會是亂序
for(int i=0;i<5;i++){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"global: %d %@",i, [NSThread currentThread]);
});
}
//2018-04-30 16:42:58.907260+0800 NSTimer_NSRunLoop[4045:161039] global: 3 <NSThread: 0x604000460240>{number = 5, name = (null)}
//2018-04-30 16:42:58.907258+0800 NSTimer_NSRunLoop[4045:161045] global: 1 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:42:58.907299+0800 NSTimer_NSRunLoop[4045:161061] global: 4 <NSThread: 0x60000007d080>{number = 6, name = (null)}
//2018-04-30 16:42:58.907258+0800 NSTimer_NSRunLoop[4045:161040] global: 2 <NSThread: 0x60000007cf00>{number = 4, name = (null)}
//2018-04-30 16:42:58.907258+0800 NSTimer_NSRunLoop[4045:161038] global: 0 <NSThread: 0x604000460040>{number = 2, name = (null)}
sleep(1);
//自行建立serial queue,會按照順序執行工作
dispatch_queue_t serialQueue = \
dispatch_queue_create("thread4", DISPATCH_QUEUE_SERIAL);
for(int i=0;i<5;i++){
dispatch_async(serialQueue, ^{
NSLog(@"serial queue %d %@",i, [NSThread currentThread]);
});
}
//2018-04-30 16:42:59.908440+0800 NSTimer_NSRunLoop[4045:161045] serial queue 0 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:42:59.908514+0800 NSTimer_NSRunLoop[4045:161045] serial queue 1 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:42:59.908541+0800 NSTimer_NSRunLoop[4045:161045] serial queue 2 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:42:59.908561+0800 NSTimer_NSRunLoop[4045:161045] serial queue 3 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:42:59.908579+0800 NSTimer_NSRunLoop[4045:161045] serial queue 4 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
sleep(1);
//自行建立concurrent queue
dispatch_queue_t concurrentQueue = \
dispatch_queue_create("thread5", DISPATCH_QUEUE_CONCURRENT);
for(int i=0;i<5;i++){
dispatch_async(concurrentQueue, ^{
NSLog(@"concurrent queue %d %@",i, [NSThread currentThread]);
});
}
//2018-04-30 16:43:00.909678+0800 NSTimer_NSRunLoop[4045:161045] concurrent queue 0 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
//2018-04-30 16:43:00.909678+0800 NSTimer_NSRunLoop[4045:161040] concurrent queue 2 <NSThread: 0x60000007cf00>{number = 4, name = (null)}
//2018-04-30 16:43:00.909746+0800 NSTimer_NSRunLoop[4045:161061] concurrent queue 3 <NSThread: 0x60000007d080>{number = 6, name = (null)}
//2018-04-30 16:43:00.909678+0800 NSTimer_NSRunLoop[4045:161038] concurrent queue 1 <NSThread: 0x604000460040>{number = 2, name = (null)}
//2018-04-30 16:43:00.909757+0800 NSTimer_NSRunLoop[4045:161045] concurrent queue 4 <NSThread: 0x60000007d5c0>{number = 3, name = (null)}
view raw GCD.m hosted with ❤ by GitHub
當函式使用非同步模式,又必須回傳其運作結果時,需要使用dispatch_semaphore_t
類似於async await,之後會詳細描述

http://www.iosxxx.com/blog/2016-06-02-GCD那些事.html

沒有留言:

張貼留言