串行隊列程序員
先進先出
的方式,順序
調度隊列中的任務執行dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
/** 提問:是否開線程?是否順序執行?come here 的位置? */ - (void)gcdDemo1 { // 1. 隊列 dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL); // 2. 執行任務 for (int i = 0; i < 10; ++i) { NSLog(@"--- %d", i); dispatch_sync(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here"); }
/** 提問:是否開線程?是否順序執行?come here 的位置? */ - (void)gcdDemo2 { // 1. 隊列 dispatch_queue_t q = dispatch_queue_create("itheima", NULL); // 2. 執行任務 for (int i = 0; i < 10; ++i) { NSLog(@"--- %@ %d", [NSThread currentThread], i); dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here"); }
先進先出
的方式,併發
調度隊列中的任務執行同步
執行的,會等待任務執行完成後,再調度後續的任務異步
執行的,同時底層線程池有可用的線程資源,會再新的線程調度後續任務的執行dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);
/** 提問:是否開線程?是否順序執行?come here 的位置? */ - (void)gcdDemo3 { // 1. 隊列 dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT); // 2. 執行任務 for (int i = 0; i < 10; ++i) { dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here"); }
/** 提問:是否開線程?是否順序執行?come here 的位置? */ - (void)gcdDemo4 { // 1. 隊列 dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT); // 2. 執行任務 for (int i = 0; i < 10; ++i) { dispatch_sync(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %i", i); } NSLog(@"come here"); }
先進先出
的方式,在主線程空閒時
纔會調度隊列中的任務在主線程執行dispatch_queue_t queue = dispatch_get_main_queue();
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self gcdDemo1]; [NSThread sleepForTimeInterval:1]; NSLog(@"over"); } - (void)gcdDemo1 { dispatch_queue_t queue = dispatch_get_main_queue(); for (int i = 0; i < 10; ++i) { dispatch_async(queue, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %d", i); } NSLog(@"come here"); } 2015-07-13 00:44:57.241 testGCD線程[37988:581895] ---> 0 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 1 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 2 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 3 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 4 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 5 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 6 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 7 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 8 2015-07-13 00:44:57.242 testGCD線程[37988:581895] ---> 9 2015-07-13 00:44:57.242 testGCD線程[37988:581895] come here 2015-07-13 00:44:58.243 testGCD線程[37988:581895] over 2015-07-13 00:44:58.244 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 0 2015-07-13 00:44:58.244 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 1 2015-07-13 00:44:58.244 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 2 2015-07-13 00:44:58.244 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 3 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 4 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 5 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 6 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 7 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 8 2015-07-13 00:44:58.245 testGCD線程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 9
在
主線程空閒時
纔會調度隊列中的任務在主線程執行面試
// MARK: 主隊列,同步任務 - (void)gcdDemo6 { // 1. 隊列 dispatch_queue_t q = dispatch_get_main_queue(); NSLog(@"!!!"); // 2. 同步 dispatch_sync(q, ^{ NSLog(@"%@", [NSThread currentThread]); }); NSLog(@"come here"); }
主隊列
和主線程
相互等待會形成死鎖數組
同步任務,可讓其餘異步執行的任務,
依賴
某一個同步任務安全
例如:在用戶登陸以後,再異步下載文件!多線程
- (void)gcdDemo1 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"登陸 %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載 A %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載 B %@", [NSThread currentThread]); }); }
- (void)gcdDemo2 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); void (^task)() = ^{ dispatch_sync(queue, ^{ NSLog(@"登陸 %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載 A %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載 B %@", [NSThread currentThread]); }); }; dispatch_async(queue, task); }
- (void)gcdDemo3 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); void (^task)() = ^ { dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"死?"); }); }; dispatch_async(queue, task); }
主隊列在
主線程空閒時
纔會調度隊列中的任務在主線程執行併發Barrier 異步
非線程安全
的對象進行更新大規模的 I/O
操做@interface ViewController () { // 加載照片隊列 dispatch_queue_t _photoQueue; } @property (nonatomic, strong) NSMutableArray *photoList; @end - (NSMutableArray *)photoList { if (_photoList == nil) { _photoList = [[NSMutableArray alloc] init]; } return _photoList; }
NSMutableArray
是非線程安全的框架
- (void)viewDidLoad { [super viewDidLoad]; _photoQueue = dispatch_queue_create("com.itheima.com", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 20; ++i) { [self loadPhotos:i]; } }
- (void)loadPhotos:(int)index { dispatch_async(_photoQueue, ^{ [NSThread sleepForTimeInterval:1.0]; NSString *fileName = [NSString stringWithFormat:@"%02d.jpg", index % 10 + 1]; NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil]; UIImage *image = [UIImage imageWithContentsOfFile:path]; [self.photoList addObject:image]; NSLog(@"添加照片 %@", fileName); }); }
運行測試異步
因爲 NSMutableArray
是非線程安全的,若是出現兩個線程在同一時間向數組中添加對象,會出現程序崩潰的狀況async
解決辦法函數
NSLog(@"添加照片 %@", fileName); dispatch_barrier_async(_photoQueue, ^{ [self.photoList addObject:image]; NSLog(@"OK %@", [NSThread currentThread]); });
使用
dispatch_barrier_async
添加的 block 會在以前添加的 block 所有運行結束以後,纔在同一個線程順序執行,從而保證對非線程安全的對象進行正確的操做!
注意:
dispatch_barrier_async
必須使用自定義隊列,不然執行效果和全局隊列一致
併發隊列
一致全局隊列
NSThread
的 name
屬性做用相似dispatch_release(q);
釋放相應的對象dispatch_barrier
必須使用自定義的併發隊列/** 提問:是否開線程?是否順序執行?come here 的位置? */ - (void)gcdDemo8 { // 1. 隊列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // 2. 執行任務 for (int i = 0; i < 10; ++i) { dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here"); }
運行效果與併發隊列相同
服務質量(隊列對任務調度的優先級)/iOS 7.0 以前,是優先級
QOS_CLASS_USER_INTERACTIVE
0x21, 用戶交互(但願最快完成-不能用太耗時的操做)QOS_CLASS_USER_INITIATED
0x19, 用戶指望(但願快,也不能太耗時)QOS_CLASS_DEFAULT
0x15, 默認(用來底層重置隊列使用的,不是給程序員用的)QOS_CLASS_UTILITY
0x11, 實用工具(專門用來處理耗時操做!)QOS_CLASS_BACKGROUND
0x09, 後臺QOS_CLASS_UNSPECIFIED
0x00, 未指定,能夠和iOS 7.0 適配DISPATCH_QUEUE_PRIORITY_HIGH
2 高優先級DISPATCH_QUEUE_PRIORITY_DEFAULT
0 默認優先級DISPATCH_QUEUE_PRIORITY_LOW
(-2) 低優先級DISPATCH_QUEUE_PRIORITY_BACKGROUND
INT16_MIN 後臺優先級爲將來保留使用的,應該永遠傳入0
結論:若是要適配 iOS 7.0 & 8.0,使用如下代碼:
dispatch_get_global_queue(0, 0);
延遲操做
// MARK: - 延遲執行 - (void)delay { /** 從如今開始,通過多少納秒,由"隊列"調度異步執行 block 中的代碼 參數 1. when 從如今開始,通過多少納秒 2. queue 隊列 3. block 異步執行的任務 */ dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); void (^task)() = ^ { NSLog(@"%@", [NSThread currentThread]); }; // 主隊列 // dispatch_after(when, dispatch_get_main_queue(), task); // 全局隊列 // dispatch_after(when, dispatch_get_global_queue(0, 0), task); // 串行隊列 dispatch_after(when, dispatch_queue_create("itheima", NULL), task); NSLog(@"come here"); } - (void)after { [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0]; NSLog(@"come here"); }
有的時候,在程序開發中,有些代碼只想從程序啓動就只執行一次,典型的應用場景就是「單例」
// MARK: 一次性執行 - (void)once { static dispatch_once_t onceToken; NSLog(@"%ld", onceToken); dispatch_once(&onceToken, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"一次性嗎?"); }); NSLog(@"come here"); }
線程安全的!並且是蘋果公司推薦使用的
- (void)demoOnce { for (int i = 0; i < 10; ++i) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self once]; }); } }
// 使用 dispatch_once 實現單例 + (instancetype)sharedSingleton { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; } // 使用互斥鎖實現單例 + (instancetype)sharedSync { static id syncInstance; @synchronized(self) { if (syncInstance == nil) { syncInstance = [[self alloc] init]; } } return syncInstance; }
面試時只要實現上面
sharedSingleton
方法便可
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { long largeNumber = 1000 * 1000; // 測試互斥鎖 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSync]; } NSLog(@"互斥鎖: %f", CFAbsoluteTimeGetCurrent() - start); // 測試 dispatch_once start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSingleton]; } NSLog(@"dispatch_once: %f", CFAbsoluteTimeGetCurrent() - start); }
- (void)group1 { // 1. 調度組 dispatch_group_t group = dispatch_group_create(); // 2. 隊列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // 3. 將任務添加到隊列和調度組 dispatch_group_async(group, q, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"任務 1 %@", [NSThread currentThread]); }); dispatch_group_async(group, q, ^{ NSLog(@"任務 2 %@", [NSThread currentThread]); }); dispatch_group_async(group, q, ^{ NSLog(@"任務 3 %@", [NSThread currentThread]); }); // 4. 監聽全部任務完成 dispatch_group_notify(group, q, ^{ NSLog(@"OVER %@", [NSThread currentThread]); }); // 5. 判斷異步 NSLog(@"come here"); }
// MARK: - 調度組 2 - (void)group2 { // 1. 調度組 dispatch_group_t group = dispatch_group_create(); // 2. 隊列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // dispatch_group_enter & dispatch_group_leave 必須成對出現 dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任務 1 %@", [NSThread currentThread]); // dispatch_group_leave 必須是 block 的最後一句 dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任務 2 %@", [NSThread currentThread]); // dispatch_group_leave 必須是 block 的最後一句 dispatch_group_leave(group); }); // 4. 阻塞式等待調度組中全部任務執行完畢 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 5. 判斷異步 NSLog(@"OVER %@", [NSThread currentThread]); }