練習題數組
- (void)viewDidLoad { [super viewDidLoad]; __block int a = 0; while (a < 5) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } NSLog(@"a = %d", a); } 複製代碼
A、 a = 0 B、a < 5 C、a = 5 D、a > 5安全
答案:C、Dbash
答案分析markdown
打印結果:併發
看到打印結果很明顯浪費了不少性能,想直接打到正確的結果仍是有必定的風險,要解決這風險咱們能夠嘗試一下使用信號量異步
//當value 等於 1 時,會起到鎖的效果,一次一個執行。
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
// timeout = DISPATCH_TIME_FOREVER ,意味沒有回來,就一直等待,加鎖的效果
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
//解鎖
dispatch_semaphore_signal(sem);
複製代碼
優化代碼async
- (void)viewDidLoad { [super viewDidLoad]; //dispatch_semaphore_create(long value);當value 等於 1 時,會起到鎖的效果,一次一個執行。 dispatch_semaphore_t sem = dispatch_semaphore_create(1); __block int a = 0; while (a < 5) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"裏面 a = %d----%@", a, [NSThread currentThread]); a++; dispatch_semaphore_signal(sem); }); // timeout = DISPATCH_TIME_FOREVER ,意味沒有回來,就一直等待 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);// } NSLog(@"a = %d", a); } 複製代碼
打印結果:函數
答案解析性能
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
複製代碼
主要起到控制任務執行順序,起到同步效果優化
dispatch_barrier_async
前面的任務執行完畢纔會到這裏
dispatch_barrier_sync
做用相同,可是這個會堵塞線程,影響後面的任務執行
注意,柵欄函數只能控制同一併發隊列
- (void)demo2{ dispatch_queue_t concurrentQueue = dispatch_queue_create("janice", DISPATCH_QUEUE_CONCURRENT); /* 1. 異步函數 */ dispatch_async(concurrentQueue, ^{ sleep(1); NSLog(@"123"); }); /* 2. 柵欄函數 */ dispatch_barrier_async(concurrentQueue, ^{ NSLog(@"---------------------%@------------------------",[NSThread currentThread]); }); /* 3. 異步函數 */ dispatch_async(concurrentQueue, ^{ NSLog(@"加載那麼多,喘口氣!!!"); }); NSLog(@"************起來幹!!"); } 複製代碼
打印結果:
- (void)demo3{ dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- 線程BUG for (int i = 0; i<2000; i++) { dispatch_async(concurrentQueue, ^{ NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); } } 複製代碼
打印結果
有個問題,添加了2000張圖片,可是實際打印效果只有 1997,這是爲何呢,說明線程不安全。
優化辦法,在圖片加入到數組的時候,添加一個柵欄
- (void)demo3{ // 順序執行 // 線程安全 dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- 線程BUG for (int i = 0; i<2000; i++) { dispatch_async(concurrentQueue, ^{ NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); } } 複製代碼
打印結果:
就是添加一個柵欄來保障其線程安全
將自定義併發隊列換成全局併發隊列。
- (void)demo3{ dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0); // signal -- 線程BUG for (int i = 0; i<2000; i++) { dispatch_async(concurrentQueue, ^{ NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)]; NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); } } 複製代碼
崩潰了,使用柵欄函數的時候,必定要注意,必定是自定義的併發隊列,全局隊列是整個系統都在用,在這了使用的情景會就會形成堵塞,因此就會崩潰
總結:
做用:控制任務執行順序
- (void)groupDemo{ //建立調度組 dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_queue_t queue1 = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT); // SIGNAL dispatch_group_async(group, queue, ^{ NSString *logoStr = @"http://p.qpic.cn/qqcourse/QFzQYCgCrxlq7n5Jats36WGb4wxzJIYmFplnUUgdxk4gy66xicbmGCDM5DXDuVNQP/"; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); dispatch_group_async(group, queue1, ^{ sleep(2); NSString *logoStr = @"https://www.baidu.com/img/baidu_resultlogo@2.png"; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); __block UIImage *newImage = nil; dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"數組個數:%ld",self.mArray.count); for (int i = 0; i<self.mArray.count; i++) { UIImage *waterImage = self.mArray[i]; newImage = [KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)]; } self.imageView.image = newImage; }); } 複製代碼
dispatch_semaphore_t
複製代碼
基本使用
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 信號量 -- gcd控制併發數 // 同步 //總結:因爲設定的信號值爲3,先執行三個線程,等執行完一個,纔會繼續執行下一個,保證同一時間執行的線程數不超過3 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); //任務1 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"執行任務1"); sleep(1); NSLog(@"任務1完成"); dispatch_semaphore_signal(semaphore); }); //任務2 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"執行任務2"); sleep(1); NSLog(@"任務2完成"); dispatch_semaphore_signal(semaphore); }); //任務3 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"執行任務3"); sleep(1); NSLog(@"任務3完成"); dispatch_semaphore_signal(semaphore); }); } 複製代碼
打印結果:
在任一線程上調用它的一個函數 dispatch_source_merge_data 後,會執行 Dispatch Source 實現定義好的句柄(能夠把句柄簡單理解爲一個 block)這個過程叫 Custom event ,用戶事件,是dispatch source 支持處理的一種事件。
句柄是一種指向指針的指針,它指向的就是一個類或者結構,它和系統有很密切的關係
HINSTANCE(實例句柄),HBITMAP(位圖句柄),HDC(設備表述句柄),HICON(圖標句柄)等。這當中還有一個通用的句柄,就是 HANDLE,好比下面的語句:
ViewController.m
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (nonatomic, strong) dispatch_source_t source; @property (nonatomic, strong) dispatch_queue_t queue; @property (nonatomic, assign) NSUInteger totalComplete; @property (nonatomic) BOOL isRunning; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.totalComplete = 0; self.queue = dispatch_queue_create("com.tz.cn.janice", 0); 第一個參數:dispatch_source_type_t type爲設置GCD源方法的類型,前面已經列舉過了。 第二個參數:uintptr_t handle Apple的API介紹說,暫時沒有使用,傳0便可。 第三個參數:unsigned long mask Apple的API介紹說,使用DISPATCH_TIMER_STRICT,會引發電量消耗加重,畢竟要求精確時間,因此通常傳0便可,視業務狀況而定。 第四個參數:dispatch_queue_t _Nullable queue 隊列,將定時器事件處理的Block提交到哪一個隊列之上。能夠傳Null,默認爲全局隊列。注意:當提交到全局隊列的時候,時間處理的回調內,須要異步獲取UI線程,更新UI...不過這好像是常識,又囉嗦了... */ self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); // 保存代碼塊 ---> 異步 dispatch_source_set_event_handler() // 設置取消回調 dispatch_source_set_cancel_handler(dispatch_source_t source,dispatch_block_t _Nullable handler) // 封裝咱們須要回調的觸發函數 -- 響應 dispatch_source_set_event_handler(self.source, ^{ NSUInteger value = dispatch_source_get_data(self.source); // 取回來值 1 響應式 self.totalComplete += value; NSLog(@"進度:%.2f", self.totalComplete/100.0); self.progressView.progress = self.totalComplete/100.0; }); self.isRunning = YES; dispatch_resume(self.source); - (IBAction)didClickStartOrPauseAction:(id)sender { if (self.isRunning) {// 正在跑就暫停 dispatch_suspend(self.source); dispatch_suspend(self.queue);// mainqueue 掛起 self.isRunning = NO; [sender setTitle:@"暫停中..." forState:UIControlStateNormal]; }else{ dispatch_resume(self.source); dispatch_resume(self.queue); self.isRunning = YES; [sender setTitle:@"加載中..." forState:UIControlStateNormal]; } } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ NSLog(@"點擊開始加載"); for (NSUInteger index = 0; index < 100; index++) { dispatch_async(self.queue, ^{ if (!self.isRunning) { NSLog(@"暫停下載"); return ; } sleep(2); dispatch_source_merge_data(self.source, 1); // source 值響應 }); } } 複製代碼
打印結果: