iOS 多工全部執行完後再執行操作 —— HERO部落格
阿新 • • 發佈:2019-02-20
介紹一下開發中遇到多個任務的情況及處理方法。
1. 有兩個載入圖片的任務,全部載入完成後在進行相應操作,耗時操作不應該放在主執行緒,所以開啟子執行緒載入,通過佇列組實現:
{ // 建立佇列組 dispatch_group_t group = dispatch_group_create(); // 建立併發佇列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 開子執行緒,任務1 dispatch_group_async(group, queue, ^{ [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://img-blog.csdn.net/20180421152137506"]]; NSLog(@"任務1 完成,執行緒:%@", [NSThread currentThread]); }); // 開子執行緒,任務2 dispatch_group_async(group, queue, ^{ [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://img-blog.csdn.net/20170112145924755?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVyb193cWI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"]]; NSLog(@"任務2 完成,執行緒:%@", [NSThread currentThread]); }); // 全部完成 dispatch_group_notify(group, queue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"全部完成,執行緒:%@", [NSThread currentThread]); }); }); } 輸出結果: 2018-05-10 19:42:16.704148+0800 AsyTaskTest[5963:308229] 任務1 完成,執行緒:<NSThread: 0x604000263380>{number = 3, name = (null)} 2018-05-10 19:42:16.725395+0800 AsyTaskTest[5963:308228] 任務2 完成,執行緒:<NSThread: 0x60400007c4c0>{number = 4, name = (null)} 2018-05-10 19:42:16.725829+0800 AsyTaskTest[5963:308103] 全部完成,執行緒:<NSThread: 0x604000070600>{number = 1, name = main}
2. 通過NSOperation實現1中的需求,並新增依賴關係:
{ // 建立佇列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 任務1 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://img-blog.csdn.net/20180421152137506"]]; NSLog(@"任務1 完成,執行緒:%@", [NSThread currentThread]); }]; // 任務2 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://img-blog.csdn.net/20170112145924755?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVyb193cWI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"]]; NSLog(@"任務2 完成,執行緒:%@", [NSThread currentThread]); }]; // 新增操作依賴,注意不能迴圈依賴 [op1 addDependency:op2]; op1.completionBlock = ^{ NSLog(@"全部完成,執行緒:%@", [NSThread currentThread]); }; // 新增操作到佇列 [queue addOperation:op1]; [queue addOperation:op2]; } 輸出結果: 2018-05-10 19:43:00.755428+0800 AsyTaskTest[6009:309365] 任務2 完成,執行緒:<NSThread: 0x600000277c80>{number = 3, name = (null)} 2018-05-10 19:43:00.771739+0800 AsyTaskTest[6009:309362] 任務1 完成,執行緒:<NSThread: 0x60400046c0c0>{number = 4, name = (null)} 2018-05-10 19:43:00.772045+0800 AsyTaskTest[6009:309364] 全部完成,執行緒:<NSThread: 0x600000277d40>{number = 5, name = (null)}
3. 如果1中的任務本身就是非同步的,按1中操作是無法實現全部載入完成後在進行相應操作:
{ NSURLSession *session = [NSURLSession sharedSession]; // 建立佇列組 dispatch_group_t group = dispatch_group_create(); // 建立併發佇列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 任務1 dispatch_group_async(group, queue, ^{ NSURLSessionDataTask *task1 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/hero/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任務1 完成,執行緒:%@", [NSThread currentThread]); }]; [task1 resume]; }); // 任務2 dispatch_group_async(group, queue, ^{ NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/thumbnails/erin-sarofsky/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"任務2 完成,執行緒:%@", [NSThread currentThread]); }]; [task2 resume]; }); // 全部完成 dispatch_group_notify(group, queue, ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"全部完成,執行緒:%@", [NSThread currentThread]); }); }); } 輸出結果: 2018-05-10 19:43:43.282177+0800 AsyTaskTest[6048:310326] 全部完成,執行緒:<NSThread: 0x60000007f480>{number = 1, name = main} 2018-05-10 19:43:46.771187+0800 AsyTaskTest[6048:310361] 任務2 完成,執行緒:<NSThread: 0x604000468a80>{number = 3, name = (null)} 2018-05-10 19:43:47.061490+0800 AsyTaskTest[6048:310364] 任務1 完成,執行緒:<NSThread: 0x60400046a900>{number = 4, name = (null)}
4. 通過dispatch_group_enter、dispatch_group_leave實現3的需求:
{
NSURLSession *session = [NSURLSession sharedSession];
// 建立佇列組
dispatch_group_t group = dispatch_group_create();
// 任務1
dispatch_group_enter(group);
NSURLSessionDataTask *task1 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/hero/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"任務1 完成,執行緒:%@", [NSThread currentThread]);
dispatch_group_leave(group);
}];
[task1 resume];
// 任務2
dispatch_group_enter(group);
NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/thumbnails/erin-sarofsky/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"任務2 完成,執行緒:%@", [NSThread currentThread]);
dispatch_group_leave(group);
}];
[task2 resume];
// 全部完成
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
NSLog(@"全部完成,執行緒:%@", [NSThread currentThread]);
});
}
輸出結果:
2018-05-10 19:44:30.653594+0800 AsyTaskTest[6102:311543] 任務2 完成,執行緒:<NSThread: 0x60400046bd00>{number = 3, name = (null)}
2018-05-10 19:44:30.868703+0800 AsyTaskTest[6102:311539] 任務1 完成,執行緒:<NSThread: 0x60400046c700>{number = 4, name = (null)}
2018-05-10 19:44:30.868897+0800 AsyTaskTest[6102:311509] 全部完成,執行緒:<NSThread: 0x60400007cdc0>{number = 1, name = main}
5. 通過訊號量實現3的需求:
{
// 初始化訊號量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSession *session = [NSURLSession sharedSession];
// 建立佇列組
dispatch_group_t group = dispatch_group_create();
// 建立併發佇列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 任務1
dispatch_group_async(group, queue, ^{
NSURLSessionDataTask *task1 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/hero/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"任務1 完成,執行緒:%@", [NSThread currentThread]);
// 傳送訊號,使訊號量+1
dispatch_semaphore_signal(semaphore);
}];
[task1 resume];
});
// 訊號量等於0時會一直等待,大於0時正常執行,並讓訊號量-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 任務2
dispatch_group_async(group, queue, ^{
NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.apple.com/105/media/us/imac-pro/2018/d0b63f9b_f0de_4dea_a993_62b4cb35ca96/thumbnails/erin-sarofsky/large.mp4"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"任務2 完成,執行緒:%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
}];
[task2 resume];
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 全部完成
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"全部完成,執行緒:%@", [NSThread currentThread]);
});
}
輸出結果:
2018-05-10 19:45:14.503833+0800 AsyTaskTest[6149:312567] 任務1 完成,執行緒:<NSThread: 0x600000468040>{number = 3, name = (null)}
2018-05-10 19:45:14.631690+0800 AsyTaskTest[6149:312567] 任務2 完成,執行緒:<NSThread: 0x600000468040>{number = 3, name = (null)}
2018-05-10 19:45:14.636026+0800 AsyTaskTest[6149:312507] 全部完成,執行緒:<NSThread: 0x60000007d680>{number = 1, name = main}
這裡做一下解釋:
執行第3行程式碼,初始化訊號量為0;
執行第13行程式碼,非同步執行任務1;
執行第22行程式碼,訊號量為0,進入等待,不會往後執行程式碼,任務1完成,17行程式碼執行使訊號量+1變為1,通過22行程式碼,訊號量-1變為0;
執行第25行程式碼,非同步執行任務2;
執行第32行程式碼,訊號量為0,進入等待,不會往後執行程式碼,任務2完成,28行程式碼執行使訊號量+1變為1,通過32行程式碼,訊號量-1變為0;
執行第36行程式碼,全部完成。
可以看出,這樣執行,是先執行任務1,然後執行任務2。如果想同時執行任務1和任務2,只需要把22行程式碼移到33行位置。
注意一點:使用訊號量,dispatch_semaphore_signal和dispatch_semaphore_wait需要成對使用,不然會造成crash。