iOS_多線程二:GCD:notify、enter leave、semaphore、barrier、diapatch_apply等的使用

實現:等待多個耗時異步任務完成後,執行

方法1:使用notify

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(4);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_group_notify(group, queue, ^{
  NSLog(@"都完成後,執行");
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行2:<NSThread: 0x60000142efc0>{number = 5, name = (null)}
// 執行1:<NSThread: 0x600001425440>{number = 3, name = (null)}
// 完成1:<NSThread: 0x600001425440>{number = 3, name = (null)}
// 完成2:<NSThread: 0x60000142efc0>{number = 5, name = (null)}
// 都完成後,執行

方法2:使用 enter leave

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_enter(group);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成1:%@", [NSThread currentThread]);
  dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(4);
  NSLog(@"完成2:%@", [NSThread currentThread]);
  dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
  NSLog(@"都完成後,執行");
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行2:<NSThread: 0x60000334cfc0>{number = 5, name = (null)}
// 執行1:<NSThread: 0x600003344c00>{number = 6, name = (null)}
// 完成1:<NSThread: 0x600003344c00>{number = 6, name = (null)}
// 完成2:<NSThread: 0x60000334cfc0>{number = 5, name = (null)}
// 都完成後,執行

信號量semaphore 使用

參考1 參考2

用處1:等待異步回調

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 傳入的值必須>=0,否則返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); // 如果>0:則不會等待
dispatch_async(queue, ^{
  NSLog(@"執行:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成:%@", [NSThread currentThread]);
  dispatch_semaphore_signal(semaphore); // semaphore+1
});
NSLog(@"wait");
// 會阻塞當前線程(這裏是主線程)
// semaphore爲0,會等待timeout
// 等待期間 semaphore>0 or timeout 會解除阻塞繼續執行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // semaphore-1
// 設置當前時間延遲幾秒: dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10); 
NSLog(@"是否阻塞主線程"); // 會
// 執行結果:
// wait
// 執行:<NSThread: 0x60000203ca40>{number = 5, name = (null)}
// 完成:<NSThread: 0x60000203ca40>{number = 5, name = (null)}
// 是否阻塞主線程

用處2:控制併發數:

// create的value表示,最多幾個資源可訪問 (可以測試一下:1~3)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(quene, ^{
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  NSLog(@"任務1 執行");
  sleep(1);
  NSLog(@"任務1 完成");
  dispatch_semaphore_signal(semaphore);
});
dispatch_async(quene, ^{
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  NSLog(@"任務2 執行");
  sleep(2);
  NSLog(@"任務2 完成");
  dispatch_semaphore_signal(semaphore);
});
dispatch_async(quene, ^{
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  NSLog(@"任務3 執行");
  sleep(1);
  NSLog(@"任務3 完成");
  dispatch_semaphore_signal(semaphore);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 任務1 執行
// 任務2 執行
// 任務1 完成
// 任務3 執行
// 任務2 完成
// 任務3 完成

由於設定的信號值爲2,先執行兩個線程,等執行完一個,纔會繼續執行下一個,保證同一時間執行的線程數不超過2

用處3:爲線程加鎖:(性能遠高於@synchronized,僅次於OSSpinLock)

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 第1個不會wait
for (int i = 0; i < 20; i++) {
 dispatch_async(queue, ^{
  // 相當於加鎖
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 使semaphore-1
  sleep(2);
  NSLog(@"i = %d semaphore = %@", i, semaphore);
  // 相當於解鎖
  dispatch_semaphore_signal(semaphore); // 使semaphore+1
 });
}
NSLog(@"是否阻塞主線程"); // 不會
// 當第一個異步循環走到wait時,因爲semaphore>0,所以會-1,繼續執行: signal+1
// 如果下一個循環在上一個循環結束前開始, 因爲 semaphore>0, 所以會wait,直到signal+1
// 保證了log循序輸出

實現多個異步順序執行:

這裏是用group+semaphore實現的:(上一篇中用僅用隊列也可實現)

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, queue, ^{
    NSLog(@"執行1:%@", [NSThread currentThread]);
    sleep(2);
    NSLog(@"完成1:%@", [NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
    NSLog(@"執行2:%@", [NSThread currentThread]);
    sleep(3);
    NSLog(@"完成2:%@", [NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
    NSLog(@"執行3:%@", [NSThread currentThread]);
    sleep(1);
    NSLog(@"完成3:%@", [NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"是否阻塞主線程"); // 會
// 執行結果:
// 執行1:<NSThread: 0x600000607fc0>{number = 4, name = (null)}
// 完成1:<NSThread: 0x600000607fc0>{number = 4, name = (null)}
// 執行2:<NSThread: 0x600000607fc0>{number = 4, name = (null)}
// 完成2:<NSThread: 0x600000607fc0>{number = 4, name = (null)}
// 執行3:<NSThread: 0x600000650000>{number = 8, name = (null)}
// 是否阻塞主線程

barrier 柵欄的使用

dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
  NSLog(@"柵欄");
});
dispatch_async(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 會
// 執行結果
// 執行1:<NSThread: 0x6000016433c0>{number = 5, name = (null)}
// 執行2:<NSThread: 0x60000167cbc0>{number = 6, name = (null)}
// 完成1:<NSThread: 0x6000016433c0>{number = 5, name = (null)}
// 完成2:<NSThread: 0x60000167cbc0>{number = 6, name = (null)}
// 柵欄
// 是否阻塞主線程
// 執行3:<NSThread: 0x60000167cbc0>{number = 6, name = (null)}
// 完成3:<NSThread: 0x60000167cbc0>{number = 6, name = (null)}
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
  NSLog(@"執行1:%@", [NSThread currentThread]);
  sleep(2);
  NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
  NSLog(@"執行2:%@", [NSThread currentThread]);
  sleep(3);
  NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
  NSLog(@"柵欄");
});
dispatch_async(queue, ^{
  NSLog(@"執行3:%@", [NSThread currentThread]);
  sleep(1);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果
// 是否阻塞主線程
// 執行1:<NSThread: 0x600002810bc0>{number = 3, name = (null)}
// 執行2:<NSThread: 0x60000281ad40>{number = 5, name = (null)}
// 完成1:<NSThread: 0x600002810bc0>{number = 3, name = (null)}
// 完成2:<NSThread: 0x60000281ad40>{number = 5, name = (null)}
// 柵欄
// 執行3:<NSThread: 0x60000281ad40>{number = 5, name = (null)}
// 完成3:<NSThread: 0x60000281ad40>{number = 5, name = (null)}

diapatch_apply使用:

往隊列中添加任務,任務會重複執行n次

// 例:處理數組
NSArray *array = @[@"a", @"b", @"c", @"d"];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(array.count, queue, ^(size_t index) { // index倒序
  NSLog(@"index:%zu %@", index, array[index]);
});
NSLog(@"是否阻塞主線程 dispatch_apply"); // 會

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章