多线程

2021-08-19  本文已影响0人  雪碧童鞋

同步、异步、并发、串行

同步和异步决定能否开启新的线程
同步: 在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力
并发和串行主要影响任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务

并发队列 手动创建串行队列 主队列
同步(sync) 没有开启新线程<br />串行执行任务 没有开启新线程<br />串行执行任务 没有开启新线程<br />串行执行任务
异步(async) 开启新线程<br />并发执行任务 开启新线程<br />串行执行任务 没有开启新线程<br />串行执行任务

使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

1. dispatch_after

dispatch_after表示在某队列中的block延迟执行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"2秒后输出");
});

2. dispatch_once

dispatch_once保证在App运行期间,block中的代码只执行一次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    //创建单例、method swizzled或其他任务
});

3. dispatch_apply

dispatch_apply将指定的Block追加到指定的队列中重复执行,并等到全部的处理执行结束。
模拟for循环,当要对NSArray类对象的所有元素执行处理时,不必一个一个的编写for循环部分

 //1.创建NSArray类对象
     NSArray *array = @[@"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j"];
 
     //2.创建一个全局队列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
     //3.通过dispatch_apply函数对NSArray中的全部元素进行处理,并等待处理完成
     dispatch_apply([array count], queue, ^(size_t index) {
         NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
     });
   //4.会在上面执行完之后执行
     NSLog(@"done");

4. dispatch_group_t

应用场景:多个接口请求之后刷新页面

1. dispatch_group_async
// 创建队列组
    dispatch_group_t group = dispatch_group_create();
    // 创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 添加异步任务
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务2-%@", [NSThread currentThread]);
        }
    });
    // 等前面的任务执行完毕后,会自动执行这个任务
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3-%@", [NSThread currentThread]);
        }
    });
2. dispatch_group_enter & dispatch_group_leave
 dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"请求一完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"请求二完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新页面");
    });
3.dispatch_group_wait使用
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
- (void)test {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"请求一完成");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"请求二完成");
        dispatch_group_leave(group);
    });
    //不等待,立即判断是否执行完毕
    long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW);
    // 等到队列组执行完毕
//    long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //指定时间内是否执行完毕
//    long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
    NSLog(@"timeout=%ld", timeout);
    if (timeout == 0) {
        NSLog(@"按时完成任务");
    } else {
        NSLog(@"超时");
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新页面");
    });
}

5. 栅栏函数dispatch_barrier_sync & dispatch_barrier_async

先执行栅栏前任务,再执行栅栏任务,最后执行栅栏后任务
应用场景:同步锁

注意点:

  1. 使用全局队列起不到栅栏函数的作用
  2. 使用全局队列时由于对全局队列造成堵塞,可能致使系统其他调用全局队列的地方也堵塞从而导致崩溃(并不是只有你在使用这个队列)
  3. 栅栏函数只能控制同一并发队列
- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("HJTest", DISPATCH_QUEUE_CONCURRENT);

    NSLog(@"开始——%@", [NSThread currentThread]);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"延迟2s的任务1——%@", [NSThread currentThread]);
    });
    NSLog(@"第一次结束——%@", [NSThread currentThread]);

    dispatch_barrier_async(queue, ^{
        NSLog(@"----------栅栏任务----------%@", [NSThread currentThread]);
    });
    NSLog(@"栅栏结束——%@", [NSThread currentThread]);

    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"延迟1s的任务2——%@", [NSThread currentThread]);
    });
    NSLog(@"第二次结束——%@", [NSThread currentThread]);
}

由于并发队列异步执行任务是乱序执行完毕的,所以使用栅栏函数可以很好的控制队列内任务执行的顺序

6. dispatch_semaphore_t

创建信号量时传入值为1时,可以通过两次才堵塞,传入值为2时,可以通过三次才堵塞

以下代码也可以使用栅栏函数实现

 // 创建信号量
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_queue_t queue = dispatch_queue_create("HJTest", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"当前%d----线程%@", i, [NSThread currentThread]);
            // 打印任务结束后信号量解锁
            dispatch_semaphore_signal(sem);
        });
        // 由于异步执行,打印任务会较慢,所以这里信号量加锁
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    }

7. dispatch_source

1.定义及使用

dispatch_source是一种基本的数据类型,可以用来监听一些底层的系统事件

主要使用的API:

@property (nonatomic, strong) dispatch_source_t timer;
//1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.创建timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//3.设置timer首次执行时间,间隔,精确度 leeway:期望容忍值
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
//4.设置timer事件回调
dispatch_source_set_event_handler(_timer, ^{
    NSLog(@"GCDTimer");
});
//5.默认是挂起状态,需要手动激活
dispatch_resume(_timer);

使用dispatch_source自定义定时器注意点:

上一篇下一篇

猜你喜欢

热点阅读