当前位置: 首页 > news >正文

[iOS] GCD - 线程与队列

[iOS] GCD - 线程与队列

文章目录

  • [iOS] GCD - 线程与队列
    • 一、线程与队列的关系
      • 1.一个队列对应一个线程
      • 2.一个队列对应两个线程
      • 3.两个队列对应一个线程
      • 4.两个队列对应两个线程
    • 二、主线程特点导致死锁
      • 1.主线程死锁,这里主要针对的是主队列
      • 2.同一个串行队列,同步执行任务嵌套,造成死锁(补)
    • 总结

在 iOS 开发中线程和队列有种说不清道不明的关系。

线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进行执行。 我的理解:多个队列的任务可以在一条线程上执行,一个队列的任务也可以在多条线程上执行。个人理解,队列可以包含线程,线程也可以包含队列。

下面有两个 GCD 相关的函数

dispatch_sync:立马在当前线程执行任务,执行完再往下走,这句话可以解释很多问题。(同步)

dispatch_async:不要求立马在当前线程执行任务,可能会开启新线程,也有可能不会。(异步)

一、线程与队列的关系

1.一个队列对应一个线程

“主队列” 对应 “主线程”

- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor blueColor];[self treadTest];// Do any additional setup after loading the view.
}
- (void) treadTest {NSLog(@"我是test1");NSLog(@"我是test2");
}

img

2.一个队列对应两个线程

“队列1” 对应 “主线程"和"新线程” (因为主队列没有开启新线程的能力所以用"队列1")

- (void)threadTest{dispatch_queue_t queue = dispatch_queue_create("队列1", DISPATCH_QUEUE_SERIAL);//同步 不开启新线程,所以在主线程执行dispatch_sync(queue, ^{NSLog(@"任务1");[self getCurrentThread];});//异步 开启新线程dispatch_async(queue, ^{NSLog(@"任务2");//2[self getCurrentThread];});sleep(3);//如果没有这个方法,2,3的执行先后顺序是不确定的,因为是两个线程,执行先后没有关系NSLog(@"方法执行结束");//3
}
- (void)getCurrentThread{NSThread *currentThread = [NSThread currentThread];NSLog(@"currentThread == %@",currentThread);
}
任务1
currentThread == <_NSMainThread: 0x600001708000>{number = 1, name = main}
任务2
currentThread == <NSThread: 0x600001720840>{number = 6, name = (null)}
方法执行结束

一个队列里面的两个任务被两个线程所执行,没有所谓的先后顺序,完全取决于他的执行时长,两个任务之前毫无关系。

3.两个队列对应一个线程

- (void) threadTest {dispatch_queue_t queue = dispatch_queue_create("队列 1", DISPATCH_QUEUE_SERIAL);NSLog(@"这是任务2");[self getCurrentThread];dispatch_sync(queue, ^{
//        sleep(3);NSLog(@"这是任务 1");[self getCurrentThread];});NSLog(@"方法执行结束");}
- (void)getCurrentThread{NSThread *currentThread = [NSThread currentThread];NSLog(@"currentThread == %@",currentThread);
}
这是任务2
currentThread == <_NSMainThread: 0x600001714000>{number = 1, name = main}
这是任务 1
currentThread == <_NSMainThread: 0x600001714000>{number = 1, name = main}
方法执行结束

在这里主线程正在执行主队列的任务 2,在这时队列 1 将任务1插入,主线程执行完任务1 后继续执行主队列的任务。

4.两个队列对应两个线程

- (void) threadTest {dispatch_queue_t queue = dispatch_queue_create("队列1", DISPATCH_QUEUE_SERIAL);NSLog(@"任务2");[self getCurrentThread];dispatch_async(queue, ^{
//        sleep(3);NSLog(@"任务1");[self getCurrentThread];});NSLog(@"方法执行结束");
}
- (void)getCurrentThread{NSThread *currentThread = [NSThread currentThread];NSLog(@"currentThread == %@",currentThread);
}
任务2
currentThread == <_NSMainThread: 0x600001708080>{number = 1, name = main}
方法执行结束
任务1
currentThread == <NSThread: 0x60000170a180>{number = 3, name = (null)}

在这里两个线程和两个队列,他们的执行毫不相干,执行完成顺序完全取决于执行时间的长短。

二、主线程特点导致死锁

主线程特点:如果主线程里有任务就必须等主线程任务执行完才轮到主队列(如果是其他队列的任务,那么任务就不用等待,会直接被主线程执行)的。所以说如果在主队列异步(开启新线程)执行任务无所谓,但是如果在主队列同步(不开启新线程,需要在主线程执行)执行任务会循环等待,造成死锁(但是在一般串行队列这样执行就不会出问题,一切都是因为主线程的这个特点)。

那这里我顺便把死锁说了

1.主线程死锁,这里主要针对的是主队列

这里的死锁是 GCD 所导致的死锁

  • 你(主线程)开始执行 任务 A 里的代码。
  • 任务 A 执行到一半,你读到了一个特殊的指令(dispatch_sync):
  • 这个指令说:“请现在往你的任务清单(主队列)的末尾,再加一个 任务 B,并且你(主线程)必须停下手头所有的工作,站在这里,亲眼等到 任务 B 被完成,然后才能继续做 任务 A
  • 你(主线程 )遵从了指令:(在这里导致了死锁的发生 )
    • 你把 任务 B 写在了 任务 A后面
    • 停下了手头正在做的 任务 A,开始傻等
    • 你在等什么?你在等 任务 B 被完成。
  • 任务清单(主队列)现在的状态是:
  1. 任务 A (正在执行中,但被卡住了…)
  2. 任务 B (在后面排队…)
  • 悖论出现了:
    • 任务 B 怎么样才能被执行?
    • 它必须等 任务 A 执行完毕。(因为这是个串行清单)
    • 任务 A 怎么样才能执行完毕?
    • 它必须等 任务 B 执行完毕。(因为 dispatch_sync 命令它等)

所以这就导致了主线程在那循环等死。

下面我给出代码

//主队列  同步执行任务  死锁
dispatch_sync(dispatch_get_main_queue(), ^{
});

2.同一个串行队列,同步执行任务嵌套,造成死锁(补)

dispatch_queue_t queue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
//串行队列  同步执行任务 因为没有开启线程所以还是主线程
dispatch_sync(queue, ^{NSLog(@"1");dispatch_sync(queue, ^{NSLog(@"串行队列嵌套执行");});NSLog(@"2");
});

上面导致的死锁就是,我们在串行队列中执行了一个任务,在这时我们又添加了一个任务造成了死锁,这个和主线程死锁没什么区别,只不过主队列是自带的,这个是我们自己创造的。

同步异步:指的是函数(方法),能否开启新的线程。同步不能,异步可以

串行并行:指的是队列,任务的执行方式,串行指各个任务按顺序执行。并行指多个任务可以同时执行

串行队列也可以开启新的线程,只不过开启之后也只是按顺序执行,

总结

在这里我只介绍了串行和同步的组合方式在后续会继续介绍剩下的三种。这篇文章也主要专注于前半部分的线程和队列关系的理解,后续文章会更加系统的来介绍异步,同步,串行,并行,他们几个之间的组合。

http://www.dtcms.com/a/577012.html

相关文章:

  • DHTMLX Gantt v9.1 正式发布:聚焦易用性与灵活性,打造更高效的项目管理体验
  • 团队介绍网站模板网站开发学什么语言
  • [AI 应用平台] Dify 在金融、教育、医疗行业的典型应用场景
  • Kiro 安全最佳实践:守护代理式 IDE 的 “防火墙”
  • 【Go】--文件和目录的操作
  • Go 语言变量作用域
  • 23、【Ubuntu】【远程开发】内网穿透:SSH 反向隧道
  • 【Linux】不允许你还不会实现shell的部分功能
  • Jmeter+ant+Jenkins 接口自动化框架-利用ant工具批量跑指定目录下的Jmeter 脚本
  • 网站建设制作 企业站开发哪家好兰州又发现一例
  • LeetCode 刷题【146. LRU 缓存】
  • 网站建设 招标公告c2c的代表性的电商平台
  • RedisCluster客户端路由智能缓存
  • K8s从Docker到Containerd的迁移全流程实践
  • Rust语言高级技巧 - RefCell 是另外一个提供了内部可变性的类型,Cell 类型没办法制造出直接指向内部数据的指针,为什么RefCell可以呢?
  • 【Python后端API开发对比】FastAPI、主流框架Flask、Django REST Framework(DRF)及高性能框架Tornado
  • 计算机外设与CPU通信
  • 玩转Rust高级应用 如何编译器对于省略掉的生命周期,不使用“自动推理”策略呢?
  • Python全栈项目:基于Django的电子商务平台开发
  • 网站建设怎么开票网站设计网页设计公司
  • Python实现GPT自动问答与保存
  • 深度强化学习,用神经网络代替 Q-table
  • seo网站建设技巧电线电缆技术支持中山网站建设
  • supabase外键查询语句
  • 【linux端cursor CLI常用命令】
  • 表的增删改查
  • Git 工作区、暂存区和版本库
  • MIT-矩阵链相乘
  • Go语言实战:入门篇-5:函数、服务接口和Swagger UI
  • 国产化Excel处理控件Spire.XLS教程:使用Java将CSV转换为PDF(含格式设置)