iOS 快速复习GCD

多线程-串行、并行队列,同步、异步任务

1、创建串行队列和并行队列

arduino 复制代码
    //并行队列
    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、同步异步任务

objectivec 复制代码
//同步
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,则继续执行,否则继续等待。

用法:

scss 复制代码
- (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     1和3不确定顺序
如果初始化创建是信号量值为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:

scss 复制代码
- (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:

scss 复制代码
- (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_t source) //得到dispatch源创建,即调用dispatch_source_create的第二个参数

  • unsignedlong dispatch_source_get_mask(dispatch_source_t source); //得到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 事件处理的调度队列

用法:

ini 复制代码
self.sourceAdd = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));

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

scss 复制代码
//需要注意循环引用
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模式应该满足使用。

代码:

scss 复制代码
//创建源
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);
}
相关推荐
DisonTangor15 小时前
苹果发布iOS 18.2首个公测版:Siri接入ChatGPT、iPhone 16拍照按钮有用了
ios·chatgpt·iphone
- 羊羊不超越 -15 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_865854881 天前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom2 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈2 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403552 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
pb82 天前
引入最新fluwx2.5.4的时候报错
flutter·ios