多线程-串行、并行行列,同步、异步使命

1、创立串行行列和并行行列

    //并行行列
    dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
     //串行行列
    dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_SERIAL);
  • 每次只有一个使命被履行。让使命一个接着一个地履行。(只敞开一个线程,一个使命履行结束后,再履行下一个使命)
  • 能够让多个使命并发(一起)履行。(能够敞开多个线程,而且一起履行使命),并发行列的并发功能只有在异步(dispatch_async)办法下才有效。

2、同步异步使命

//同步
dispatch_sync(queue, ^{
    NSLog(@"1");
  });
//异步
dispatch_async(queue, ^{
    NSLog(@"1");
  });

同步履行:

  • 同步添加使命到指定的行列中,在添加的使命履行结束之前,会一直等候,直到行列里边的使命完结之后再持续履行。
  • 只能在当时线程中履行使命,不具备敞开新线程的才能。

异步履行:

  • 异步添加使命到指定的行列中,它不会做任何等候,能够持续履行使命。
  • 能够在新的线程中履行使命,具备敞开新线程的才能。

异步履行(async) 尽管具有敞开新线程的才能,但是并不一定敞开新线程。这跟使命所指定的行列类型有关。
默认大局并发行列:dispatch_get_global_queue
第一个参数表示行列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT
第二个参数暂时没用,用0即可。

区别 并发行列 串行行列 主行列
同步(sync) 没有敞开新线程,串行履行使命 没有敞开新线程串行履行使命 死锁卡住不履行
异步(async) 有敞开新线程,并发履行使命 有敞开新线程(1条)串行履行使命 没有敞开新线程串行履行使命

信号量 dispatch_semaphore_t

GCD中的信号量dispatch_semaphore_t中首要有三个函数:

  • dispatch_semaphore_create:创立信号
  • dispatch_semaphore_wait:等候信号
  • dispatch_semaphore_signal:开释信号

1、dispatch_semaphore_create
参数为int,表示信号量初始值,需大于等于0,不然创立失利,回来一个dispatch_semaphore_t

2、dispatch_semaphore_wait
参数1:
需传递一个 dispatch_semaphore_t 类型目标,对信号进行减1,然后判别信号量巨细
参数2:
传递一个超时时刻:dispatch_time_t 目标

  • 减1后信号量小于0,则堵塞当时线程,直到超时时刻到达或者信号量大于等于0后持续履行后边代码
  • 减1后信号量大于等于0,对dispatch_semaphore_t 进行赋值,并回来dispatch_semaphore_t目标,持续履行后边代码

3、dispatch_semaphore_signal
参数:dispatch_semaphore_t
进行信号量加1操作,假如加1后成果大于等于0,则持续履行,不然持续等候。

用法:

- (void)startAsync{
    //创立信号量 值为0
  self.sem = dispatch_semaphore_create(0);
    //敞开异步并发线程履行
  dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"dispatch_semaphore 2\n");
    sleep(5);
        //发送信号,信号量值+1
    dispatch_semaphore_signal(self.sem);
    NSLog(@"dispatch_semaphore 3\n");
  });
  NSLog(@"dispatch_semaphore 0\n");
    //信号量 值-1 小于0 等候信号。。。
  dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
  NSLog(@"dispatch_semaphore 1\n");
}
履行次序0 2 1 3     13不确定次序
假如初始化创立是信号量值为1
履行次序0 1 2 3

常用总结:
1、异步并发线程次序履行
2、异步并发线程控制最大并发数,比方下载功能控制最大下载数

调度组 dispatch_group_t

首要API:

  • dispatch_group_create:创立组

  • dispatch_group_async:进组使命

  • dispatch_group_notify:组使命履行结束的通知

  • dispatch_group_enter:进组

  • dispatch_group_leave:出组

  • dispatch_group_wait:等候组使命时刻

组合用法1:

- (void)dispatchGroupAsync{
    //创立调度组
  dispatch_group_t group = dispatch_group_create();
//获取大局并发行列
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//敞开异步线程
  dispatch_group_async(group, queue, ^{
    sleep(2);
    NSLog(@"11");
  });
  dispatch_group_async(group, queue, ^{
    sleep(1);
    NSLog(@"12");
  });
  dispatch_group_async(group, queue, ^{
    sleep(3);
    NSLog(@"13");
  });
  NSLog(@"14");
  dispatch_group_notify(group, queue, ^{
    //收到履行完结的通知后履行
    NSLog(@"15");
  });
    //等候调度组履行完结
  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    调度组履行完结后履行
  NSLog(@"16");
}

用法2:

- (void)dispatchSyncEnterGroup{
    //创立调度组
  dispatch_group_t group = dispatch_group_create();
//获取大局并发行列
  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//进入调度组
  dispatch_group_enter(group);
//履行异步使命
  dispatch_async(queue, ^{
    sleep(2);
    NSLog(@"21");
        //履行完结后立刻调度组
    dispatch_group_leave(group);
  });
  dispatch_group_enter(group);
  dispatch_async(queue, ^{
    sleep(1);
    NSLog(@"22");
    dispatch_group_leave(group);
  });
  dispatch_group_enter(group);
  dispatch_async(queue, ^{
    sleep(3);
    NSLog(@"23");
    dispatch_group_leave(group);
  });
  NSLog(@"24");
  dispatch_group_notify(group, queue, ^{
        //履行完后回调
    NSLog(@"25");
  });
  NSLog(@"26");
//等候调度组履行完结
  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  NSLog(@"27");
}

总结:
1、dispatch_group_async 是对dispatch_group_enter和dispatch_group_leave的封装
2、dispatch_group_enter和dispatch_group_leave的须成双成对的呈现

事情源 dispatch_source_t

首要API:

  • dispatch_source_create :创立源

  • dispatch_source_set_event_handler: 设置源的回调

  • dispatch_source_merge_data: 源事情设置数据

  • dispatch_source_get_data: 获取源事情的数据

  • dispatch_resume:康复持续

  • dispatch_suspend:挂起

  • uintptr_t dispatch_source_get_handle(dispatch_source_tsource) //得到dispatch源创立,即调用dispatch_source_create的第二个参数

  • unsignedlongdispatch_source_get_mask(dispatch_source_tsource); //得到dispatch源创立,即调用dispatch_source_create的第三个参数

源的类型dispatch_source_type_t

    1. DISPATCH_SOURCE_TYPE_DATA_ADD:用于ADD兼并数据
    1. DISPATCH_SOURCE_TYPE_DATA_OR:用于按位或兼并数据
    1. DISPATCH_SOURCE_TYPE_DATA_REPLACE跟踪通过调用dispatch_source_merge_data取得的数据的分配源,新取得的数据值将替换 没有交付给源处理程序 的现有数据值
    1. DISPATCH_SOURCE_TYPE_MACH_SEND:用于监督Mach端口无效称号通知的调度源,只能发送没有接收权限
    1. DISPATCH_SOURCE_TYPE_MACH_RECV:用于监督Mach端口挂起音讯
    1. DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:用于监控系统内存压力变化
    1. DISPATCH_SOURCE_TYPE_PROC:用于监督外部进程的事情
    1. DISPATCH_SOURCE_TYPE_READ监督文件描述符以获取可读取的挂起字节的分配源
    1. DISPATCH_SOURCE_TYPE_SIGNAL监控当时进程以获取信号的调度源
    1. DISPATCH_SOURCE_TYPE_TIMER:基于计时器提交事情处理程序块的分配源
    1. DISPATCH_SOURCE_TYPE_VNODE:用于监督文件描述符中定义的事情的分配源
    1. DISPATCH_SOURCE_TYPE_WRITE监督文件描述符以获取可写入字节的可用缓冲区空间的分配源。

1、dispatch_source_create 参数:

  • dispatch_source_type_t要创立的源类型
  • uintptr_t 句柄 用于和其他事情并定,很少用,一般为0
  • uintptr_t mask 很少用,一般为0
  • dispatch_queue_t事情处理的调度行列

用法:

self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));

2、dispatch_source_set_event_handler 设置回调函数,当触发源事情时履行

//需求留意循环引证
dispatch_source_set_event_handler(self.sourceAdd, ^{
    需求履行的代码
});
//启动
dispatch_resume(self.sourceAdd);
//挂起,即暂停
dispatch_suspend(self.sourceAdd);
这两个API需求成对运用,不行屡次挂起或者屡次康复

3、dispatch_source_cancel 取消事情源,取消后不行再康复或挂起,需求再次创立
4、dispatch_source_set_timer 当事情源类型为定时器类型(DISPATCH_SOURCE_TYPE_TIMER)时,设置开端时刻、重复时刻、允许时刻误差

定时器实现比较简略容易,网上教程也多,这儿首要介绍一下:DISPATCH_SOURCE_TYPE_DATA_ADD、DISPATCH_SOURCE_TYPE_DATA_OR、DISPATCH_SOURCE_TYPE_DATA_REPLACE。

先说下成果:

  • DISPATCH_SOURCE_TYPE_DATA_ADD 会把事情源累加 能够记录总共发送多少次事情进行兼并
  • DISPATCH_SOURCE_TYPE_DATA_OR 会把事情源兼并,终究得到的数据源数为1
  • DISPATCH_SOURCE_TYPE_DATA_REPLACE 会用最新事情源替换旧有未处理事情,终究得到的数据源数为1
  • 循环10000次实践跑处理回调事情次数 add315 or275 replace 284

从成果上来看,当需求把快速频频的重复事情进行兼并,最好的选择是DISPATCH_SOURCE_TYPE_DATA_OR,运用场景,监听音讯时,多音讯频频下发需求改写UI,假如不进行兼并处理,会导致UI太过频频的改写,影响终究作用,且对性能开销过大。

当然,相似的场景也可运用其他办法处理,比方建立音讯池,接收音讯后标记音讯池状态及变化,然后定时从音讯池中取音讯。诸如此类的办法较多,假如只是简略的处理,上面的DISPATCH_SOURCE_TYPE_DATA_OR形式应该满意运用。

代码:

//创立源
self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
//弱引证
__weak typeof(self) weakifySelf = self;
//设置回调事情
dispatch_source_set_event_handler(self.sourceAdd, ^{
    //强引证
    __strong typeof(self) strongSelf = weakifySelf;
    //获取接收到的源数据
    strongSelf.handleData = dispatch_source_get_data(strongSelf.sourceAdd);
    NSLog(@"dispatch_source1 %ld\n",strongSelf.handleData);
//需求履行的代码
    [strongSelf sourceHandle];
    });
//敞开源
    dispatch_resume(self.sourceAdd);
for (int i = 0; i<10000; i ++) {
    [self dispatchSource];
}
- (void)dispatchSource{
  NSLog(@"dispatch_source2 %ld\n",self.handleData);
    //发送源信号
  dispatch_source_merge_data(self.sourceAdd, 1);
}