練習題數組
- (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
答案分析併發
打印結果:異步
看到打印結果很明顯浪費了不少性能,想直接打到正確的結果仍是有必定的風險,要解決這風險咱們能夠嘗試一下使用信號量async
//當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);
複製代碼
優化代碼函數
- (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);
複製代碼
主要起到控制任務執行順序,起到同步效果ui
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 值響應
});
}
}
複製代碼
打印結果: