ios线程和GCD和队列同步异步的关系

1、什么是进程?

  进程是指在系统中正在运行的一个应用程序。比如同时打开QQ、Xcode,系统就会分别启动2个进程。截图

2、什么是线程?

  1)、一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程)

  2)、线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行

  3)、一个线程中任务的执行是串行的。即如果要在1个线程中执行多个任务,那么只能一个一个的按顺序执行这些任务

3、什么是多线程?

  1)、一个线程中可以开启多条线程,每条线程可以并行执行不同的任务。比如同时开启三条线程分别下载3个文件

  2)、同一时间,CPU只能处理一条线程,只有一条线程在工作(执行)

  3)、多线程并发(同时)执行,其实是CPU快速的在多线程之间调度(切换)

  4)、如果cpu调度线程的时间足够快,就造成了多线程并发执行的假象

  5)、如果线程非常非常多,那么会发生什么情况呢?

    可能导致cpu在N条线程之间调度,消耗大量的cpu资源,进而导致每条线程被调度执行的频次会降低(线程的执行效率降低)

  6)、多线程的优点:能适当提高程序的执行效率;能适当提高资源利用率(cpu、内存利用率)

  7)、多线程的缺点:

    开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能;

    线程越多,cpu在调度线程上的开销就越大;

    程序设计更加复杂:比如线程之间的通信、多线程的数据共享

4、什么是主线程

  1)、一个ios程序运行后,默认会开启一条线程,称为”主线程“或”UI线程“

  2)、主线程的作用:

    显示/刷新UI界面;

    处理UI事件(比如点击事件、滚动事件、拖拽事件等)

  3)、主线程的使用注意:

    别将比较耗时的操作放到主线程中;耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种”卡“的坏体验;

    所以一般将耗时操作放在子线程(后台线程、非主线程)

5、ios中多线程的实现方案:

 6、NSThread

  1)一个NSThread对象就代表一条线程

  2)创建、启动线程:

    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread) object:nil];

    [thread start];

  3)、主线程相关用法:

    + (NSThread *)mainThread;  //获取主线程

    - (BOOL)isMainThread;    //是否为主线程

    + (BOOL)isMainThread;    //是否为主线程

  4)、获取当前线程: NSThread *current = [NSThread currentThread];

  5)、线程的调度优先级

    + (double)threadPriority;

    + (BOOL)setThreadPriority:(double)p;

    - (double)threadPriority;

    - (BOOL)setThreadPriority:(double)p;

    调度优先级的取值范围是0.0 ~ 1.0, 默认是0.5, 值越大,优先级越高

  6)、线程的名字:- (void)setName: (NSString *)n;   - (NSString *)name;

  7)、另外一种创建线程方法:创建线程后启动线程:

    [NSThread detachNewThreadSelector:@selector(testThread) toTarget:self withObject:nil];

  8)、第三种创建线程方式:隐式创建并启动线程:

    [self performSelectorInBackground:@selector(testThread) withObject:nil];

  9) 、后面2中创建线程方式的优缺点:

    优点:简单快捷

    缺点:无法对线程进行更详细的设置 

  10)、阻塞(暂停)线程:

    + (void)sleepUnitilDate:(NSDate *)date; //睡眠到date这个时间上

    + (void)sleepForTimeInterval:(NSTimeInterval)seconds;  //睡眠seconds秒

    调用这两个方法中任一方法,当前线程会睡眠(暂停)所设置的时间后,才会执行后面的操作

  11)、强制停止线程,让当前线程提前结束它的生命:+ (void)exit; 

 7、多线程安全问题 - 互斥锁

  1)、互斥锁使用方式:@synchronized(所对象) { //需要锁定的代码 } ;    // 线程同步:多条线程按顺序地执行任务

  2)、互斥锁的优缺点:

    优点:能有效防止因多线程抢夺同一资源造成的数据安全问题;

    缺点:需要消耗大量的CPU资源

8、原子和非原子属性

  OC在定义属性时有nonatomic和atomic两种选择

  atomic : 院子属性,为setter方法加锁(默认就是atomic)

  nonatomic:非原子属性,不会位setter方法加锁

  ---------------- nontomic 和 atomic对比 -------

  atomic :线程安全,需要消耗大量的资源;

  nonatomic : 非线程安全,适合内存晓得移动设备

  ------ 对于设置属性原子和非原子性的建议 --- 

  a、所有属性都声明位nontomic; 

  b、尽量避免多线程抢夺同一块资源;

  c、尽量将加锁、资源抢夺的业务逻辑交给服务端处理,减小移动客户端的压力。

9、线程间通信

  在1个线程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信:比如说一个线程传递数据给另一个线程,又或者在一个线程中执行完成特定任务后,转到另一个线程继续执行任务。

  线程间通信常用方法:

  - (void)performSelectorOnMainThread:(SEL)aSelectorWithObject:(id)arg waitUntilDone:(BOOL)wait; //传递数据到主线程

  - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; //传递数据到另一个线程

10、GCD

  1)、GCD,全称是Grand Central Dispatch,伟大的中央调度器。纯C语言,提供了非常多强大的函数

  2)、GCD的优势:

GCD是苹果公司位多核的并行运算提出的解决方案;会自动利用更多的CPU内核;

    会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

  3)、GCD中2个核心概念:任务和队列。任务指执行什么操作,队列是用来存放任务。

    GCD的使用步骤:

      a、定制任务。确定想做的事;    

      b、将任务添加到队列中。GCD会自动将队列中的任务取出,放到对应的线程中执行 ;任务的取出遵循队列的FIFO原则:先进先出。

    GCD执行任务函数:

      用同步的方式执行任务:dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);   // queue指队列   ;  block指任务

      用异步的方式执行任务:dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

      同步和异步的区别:同步只能在当前的线程中执行任务,不具备开启新线程的能力; 异步可以在新的线程中执行任务,具备开启新线程的能力;

  4)、队列的类型:主要分为并发队列和串行队列

    并发队列:Concurrent Dispatch Queue, 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务);

         并发功能只有在异步(dispatch_async)函数下才有效

    串行队列:Serial Dispatch Queue, 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    并行队列:

    GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建;

    使用dispatch_get_global_queue函数获得全局的并发队列;

    dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,  unsigned long flags);  //priority指队列的优先级, flags参数暂时无用,用0即可

    示例:dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,  0); //获得全局并发队列

    全局并发队列的优先级:

    #define DISPATCH_QUEUE_PRIORITY_HIGH  2//高

    #define DISPATCH_QUEUE_PRIORITY_DEFAULT  0//默认(中)

    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)  //低

    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN  //后台

    串行队列:

    使用dispatch_queue_create函数创建一个串行队列

    dispatch_queue_create(const char *lable, dispatch_queue_attr_t attr); //lable参数指队列名称;attr参数指队列属性,一般用NULL即可

    另外可以使用主队列:dispatch_get_main_queue(),主队列是GCD自带的一种特殊的串行队列;放在主队列中的任务,都会放到主线程中执行

-----------------------------------------------------

<!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px '.PingFang SC'; color: #454545} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #454545; min-height: 14.0px} span.s1 {font: 12.0px 'Helvetica Neue'} span.Apple-tab-span {white-space:pre} -->

线程和队列、异步同步函数的关系?

1、一个应用程序表示开启了一个进程

2、一个进程至少有一个线程,即至少有一个主线程,也可以开启多条线程

3、一个线程中可以有多个队列,每个队列中可以执行多个任务

4、在线程中执行任务的顺序可以是按顺序来执行,也可以用不按顺序来执行。即表现为串行和并行

5、那异步和同步函数的作用在哪里?

     是否开启多条线程,然后执行的队列(任务)是否按顺序还是不按顺序执行,都需要异步和同步函数的配合才能实现!

    并行和串行队列,与异步同步函数的排列组合有如下这些:

    a、并行队列 + 异步函数 开启多条线程,不按顺序执行任务;

    b、串行队列 + 异步函数 开启一条新线程,按顺序执行任务;

    c、主队列 + 异步函数不开启新线程,按顺序执行任务

    d、并行队列 + 同步函数不开启新线程,按顺序执行任务;

    e、串行队列 + 同步函数不开启新线程,按顺序执行任务;

    f、主队列 + 同步函数会出现卡死现象!原因:循环等待,主队列的东西要等主线程执行完,又不能开线程,

所以下面的任务要等上面的任务执行完,然后卡死

举例说明:

<!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #000000} span.s1 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #272ad8} span.s2 {font-variant-ligatures: no-common-ligatures} span.s3 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures} -->

1、并行队列 + 异步函数:开启多条线程,不按顺序执行任务 (全局队列+异步函数效果一样)

dispatch_queue_t q1 = dispatch_queue_create("mulQueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 100; i++){
        dispatch_async(q1, ^{
            NSLog(@"并行+异步,i=%d, currentThread: %@", i, [NSThread currentThread]);
        });
    }
    NSLog(@"并行队列+异步函数测试执行 end....");
    /*
     并行队列+异步函数测试执行 end....
     并行+异步,i=1, currentThread: <NSThread: 0x60000026ab80>{number = 5, name = (null)}
     并行+异步,i=0, currentThread: <NSThread: 0x600000269b00>{number = 3, name = (null)}
     并行+异步,i=4, currentThread: <NSThread: 0x60000026ea80>{number = 7, name = (null)}
     并行+异步,i=3, currentThread: <NSThread: 0x600000269bc0>{number = 6, name = (null)}
     并行+异步,i=5, currentThread: <NSThread: 0x60800026d8c0>{number = 8, name = (null)}
     ....
     并行+异步,i=97, currentThread: <NSThread: 0x600000272f80>{number = 49, name = (null)}
     并行+异步,i=99, currentThread: <NSThread: 0x600000273000>{number = 50, name = (null)}
     并行+异步,i=98, currentThread: <NSThread: 0x60000026ad40>{number = 11, name = (null)}
     */

2、并行 + 同步:不开启线程,按顺序执行,在主线程执行

dispatch_queue_t q2 = dispatch_queue_create("mulQueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 100; i++){
        dispatch_sync(q2, ^{
            NSLog(@"并行+同步,i=%d, currentThread: %@", i, [NSThread currentThread]);
        });
    }
    NSLog(@"并行队列+同步函数测试执行 end....");
    /*
     并行+同步,i=0, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     并行+同步,i=1, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     并行+同步,i=2, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     ....
     并行+同步,i=97, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     并行+同步,i=98, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     并行+同步,i=99, currentThread: <NSThread: 0x608000079040>{number = 1, name = main}
     并行队列+同步函数测试执行 end....
     */

<!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #000000} span.s1 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #272ad8} span.s2 {font-variant-ligatures: no-common-ligatures} span.s3 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures} -->

3、串行 + 异步:开启一条新线程,按顺序执行

dispatch_queue_t q3 = dispatch_queue_create("singleQueue", NULL);
    for (int i = 0; i < 100; i++){
        dispatch_async(q3, ^{
            NSLog(@"串行+异步,i=%d, currentThread: %@", i, [NSThread currentThread]);
        });
    }
    NSLog(@"串行队列+异步函数测试执行 end....: %@", [NSThread currentThread]);
    /*
     串行+异步,i=0, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     串行队列+异步函数测试执行 end....: <NSThread: 0x60800007b800>{number = 1, name = main}
     串行+异步,i=1, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     串行+异步,i=2, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     ....
     串行+异步,i=97, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     串行+异步,i=98, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     串行+异步,i=99, currentThread: <NSThread: 0x60000007f240>{number = 3, name = (null)}
     */

<!-- p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #000000} span.s1 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures} span.s2 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #272ad8} span.s3 {font-variant-ligatures: no-common-ligatures} -->

4、主队列 + 异步:不开启新线程,按顺序执行

dispatch_queue_t q4 = dispatch_get_main_queue();
    for (int i = 0; i < 100; i++){
        dispatch_async(q4, ^{
            NSLog(@"主队列+异步,i=%d, currentThread: %@", i, [NSThread currentThread]);
        });
    }
    NSLog(@"主队列+异步函数测试执行 end....: %@", [NSThread currentThread]);
    /*
     主队列+异步函数测试执行 end....: <NSThread: 0x60000006fe00>{number = 1, name = main}
     主队列+异步,i=0, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     主队列+异步,i=1, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     主队列+异步,i=2, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     ....
     主队列+异步,i=97, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     主队列+异步,i=98, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     主队列+异步,i=99, currentThread: <NSThread: 0x60000006fe00>{number = 1, name = main}
     */

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逆向技术

win32进程概念之句柄表,以及内核对象.

我们知道.我们使用CreateProcess 的时候会返回一个进程句柄.以及线程句柄. 其实在调用CreateProcess的时候.内核中会新建一个EPROCE...

982
来自专栏流柯技术学院

LoadRunner上传及下载文件

         web_submit_data("importStudent.do",

2692
来自专栏小狼的世界

Python3.6学习笔记(五)

网络程序出现的比互联网要早很多,实现方式主要依靠网络上不同主机间进程的通信,通信协议最重要的是TCP/IP协议。在这两个协议基础上还有很多更高级的协议,包括HT...

1392
来自专栏熊训德的专栏

Hbase compaction 源码分析一:compaction 概况分析

本文档从框架的源码角度梳理了,hbase 在什么情况下会触发compaction,并通过官方文档说明出发minor 和major compaction的时间点。

4881
来自专栏依乐祝

.NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入

上篇文章我给大家讲解了ASP.NET Core的概念及为什么使用它,接着带着你一步一步的配置了.NET Core的开发环境并创建了一个ASP.NET Core的...

910
来自专栏青玉伏案

iOS开发之再探多线程编程:Grand Central Dispatch详解

Swift3.0相关代码已在github上更新。之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread、操作队列以及GCD,介绍的不够深入。今...

1977
来自专栏cs

知识点回顾

1.0 java的集合 集合分为值value[collection],key-value[map]. 存储值的分为list,和set。list是线性表,包括循...

1014
来自专栏炸天帮5

win32进程概念之句柄表,以及内核对象.

我们知道.我们使用CreateProcess 的时候会返回一个进程句柄.以及线程句柄. 其实在调用CreateProcess的时候.内核中会新建一个EPROCE...

901
来自专栏大内老A

ASP.NET Core应用针对静态文件请求的处理[5]: DefaultFilesMiddleware中间件如何显示默认页面

DefaultFilesMiddleware中间件的目的在于将目标目录下的默认文件作为响应内容。我们知道,如果直接请求的就是这个默认文件,那么前面介绍的Stat...

2025
来自专栏pangguoming

Redis快速入门

Redis是一个开源,先进的key-value存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案。 Redis从它的许多竞争继承来的三个主要特点: ...

3425

扫码关注云+社区

领取腾讯云代金券