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

2022-07-20 14:09:02 浏览数 (3)

实现:等待多个耗时异步任务完成后,执行

方法1:使用wait

代码语言:javascript复制
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
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(2);
  NSLog(@"完成3:%@", [NSThread currentThread]);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// 等待所有异步事件执行完毕后继续执行
NSLog(@"是否阻塞主线程"); // 会

方法2:使用notify

代码语言:javascript复制
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, dispatch_get_main_queue(), ^{ // 切换到主线程更新UI
  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)}
// 都完成后,执行

方法3:使用 enter leave

代码语言:javascript复制
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, dispatch_get_main_queue(), ^{ // 切换到主线程更新UI
  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)}
// 都完成后,执行

方法4:串行队列

代码语言:javascript复制
dispatch_queue_t queue = dispatch_queue_create("com.mo.queue", NULL);
for (int i = 0; i < 3; i  ) {
  dispatch_async(queue, ^{
    NSLog(@"执行%i:%@", i, [NSThread currentThread]);
    sleep(1);
    NSLog(@"完成%i:%@", i, [NSThread currentThread]);
  });
}
dispatch_async(queue, ^{
  NSLog(@"以上都执行完毕");
});

信号量semaphore 使用

参考1 参考2

用处1:等待异步回调

代码语言:javascript复制
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:控制并发数:

代码语言:javascript复制
// 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)

代码语言:javascript复制
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实现的:(上一篇中用仅用队列也可实现)

代码语言:javascript复制
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 栅栏的使用

代码语言:javascript复制
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)}
代码语言:javascript复制
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次

代码语言:javascript复制
// 例:处理数组
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"); // 会

控制最大并发数

可以使用信号量来控制最大并发数(<0阻塞,>0执行)

代码语言:javascript复制
dispatch_queue_t concurrentQueue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
for (NSInteger i = 0; i < 10; i  ) {
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  dispatch_async(concurrentQueue, ^{
    NSLog(@"start %ld, %@", (long)i, [NSThread currentThread]);
    sleep(1);
    NSLog(@"end %ld, %@", (long)i, [NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
  });
}

Demo github 地址

0 人点赞