1. 程式人生 > >iOS 多工全部執行完後再執行操作 —— HERO部落格

iOS 多工全部執行完後再執行操作 —— HERO部落格

介紹一下開發中遇到多個任務的情況及處理方法。

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。