iOS 多线程【GCD】知识详解

多线程


谈到多线程,一般都会有个疑问:

线程是什么?而谈到线程,不可避免的就是会谈到另外一个词:进程。那么,什么是线程?什么是进程?两者的区别和联系?

  • 进程:是系统资源分配和调度的独立单位
  • 线程:我的理解就是线程是CPU调度和分派的基本单位

举个例子:现在有一个工厂,这个工厂有三个车间:车间A,B和C,每个车间都有4台机器。A负责生产鞋带,B负责生产鞋底,C负责生产鞋身。现在我们收到了一批订单,要生产1000双鞋。那开始生产的时候,就是车间为一个单位,获取资源开始工作,每个车间里,又分派给4台机器开始去工作进行生产。
在这里,车间就类似我们的进程。车间里的线程就是我们的线程。

因此,我们可以知道,一个进程里至少得有一条线程才可以工作。

使用多线程的好处是:可以充分利用CPU的资源进行并发工作


现在来看看iOS里面的多线程

iOS里面使用多线程主要是两种途径GCD和NSOpeartionQueue

GCD 是苹果对C语言线程的封装,使用还是利用c语言的函数来,据说特点是能够高效的利用设备的性能

NSOpeartionQueue 是苹果对GCD的封装,封装成大家熟悉的对象,直接用对象语言去操作

这里我们主要是介绍GCD

要了解GCD,首先需要了解的是四个概念:同步函数,异步函数,并发队列和串行队列

  • 同步函数 dispatch_sync 开头的函数。特点是需要这个函数的代码执行完毕,后面的代码才可以进行

  • 异步函数 dispatch_async 开头的函数。特点是后面的代码不需要等待这个函数的代码执行完毕,可以同时进行

  • 并发队列 队列里的任务可以并发执行

    并发队列的获取方式有两种:
    1.通过dispatch_queue_create函数创建

     dispatch_queue_t   queue = dispatch_queue_create("first_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    

    2.通过dispatch_get_global_queue函数可以获取系统提供的全局并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0)

  • 串行队列 队列里的任务是一个接一个执行的

串行队列的获取方式也有两种:1.通过dispatch_queue_create函数创建。 2.通过dispatch_get_main_queue函数可以获取系统提供的主队列

1.通过dispatch_queue_create函数创建
dispatch_queue_t queue = dispatch_queue_create("first_serial_queue", DISPATCH_QUEUE_DISPATCH_QUEUE_SERIAL);

2.通过dispatch_get_main_queue()函数可以获取系统提供的主队列
dispatch_queue_t queue = dispatch_get_main_queue()

这个主队列比较重要,也比较特殊。主队列里,只有一条主线程,一般情况下,在整个APP中,大部分的操作都是在这条线程上。UI控件的更新和行为都要在这个线程上执行。因此,它也叫UI线程。

在GCD里,主要是利用这四个进行组合使用,下面我们一个一个来看

  • 同步并发队列

    同步并发

    首先touchesBegan方法是在主线程执行的。从输出可以看到,同步并发队列并不会创建新的子线程,四个个任务都是顺序执行的。

  • 同步串行队列

    同步串行

    从输出看,同步串行队列也不会创建新的子线程来执行任务,并且四个任务也是顺序执行的

  • 异步并发队列

    异步并发

    从输出看,异步并发队列创建了三条线程。因为是异步执行的缘故,输出语句按照逻辑上应该是无序的。个人认为之所以end这条语句先输出,是因为异步操作开辟线程耗费了一点点时间。如果三个任务里执行的是耗时操作,那输出语句就是看哪个任务先执行完成。

  • 异步串行队列

    异步串行

    从输出结果看,异步串行,也创建了一条子线程。三个任务都是在这一条子线程上执行。逻辑上三个任务是有序的。

总结

队列 同步 异步
并发队列 不会创建子线程,顺序执行 会创建多条子线程,任务之间是并发执行
串行队列 不会创建子线程,顺序执行 只会创建一条子线程,子线程上的任务顺序执行
主队列 不会创建子线程,顺序执行 不会创建子线程,任务在主线程上顺序执行

其他函数

dispatch_group 参考来源

是可以将多个任务放在一个任务组里,进行统一的管理

  • dispatch_group_async(group, queue, block)将block任务添加到queue队列,并被group组管理
  • dispatch_group_enter(group)声明dispatch_group_enter(group)下面的任务由group组管理,group组的任务数+1
  • dispatch_group_leave(group)相应的任务执行完成,group组的任务数-1
  • dispatch_group_create()创建一个group组
  • dispatch_group_wait(group1, DISPATCH_TIME_FOREVER)
    当前线程暂停,等待dispatch_group_wait(group1, DISPATCH_TIME_FOREVER)上面的任务执行完成后,线程才继续执行
  • dispatch_group_notify(group1, queue1,block)
    监听group组中任务的完成状态,当所有的任务都执行完成后,触发block块,执行总结性处理。

dispatch_group的使用

dispatch_group_async(group,queue,block)dispatch_group_notify(group,queue,block)组合使用。

同步任务和异步任务

同步任务

输出语句hahahaha会等上面的所有任务都完成了,才会去执行。

异步任务

可以看到,因为里面的任务都是异步执行的,这个时候hahahaha不会等待上面所有的任务完成。

dispatch_group_enter(group) dispatch_group_leave(group)dispatch_group_notify(group1, queue1,block) 组合使用

dispatch_group_enter(group)
dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"====second=====%@",[NSThread currentThread]);
        });
dispatch_group_leave(group)

dispatch_group_async(group, queue, ^{
        dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"====second=====%@",[NSThread currentThread]);
        });
    });

作用是一样的。两种写法而已。


栅栏函数 dispatch_barrier_

作用是可以等这个队列里的任务都完成的时候做一个操作。


栅栏异步

栅栏同步

注意:栅栏函数使用的queue,必须是并行队列,并且不能是系统提供的并发队列

dispatch_apply 快速迭代

快速迭代

利用多线程进行的快速循环,循环次数多的情况下,更加快速。

dispatch_sem 信号量

信号量就是一个资源计数器,对信号量有两个操作来达到互斥,分别是P和V操作。 一般情况是这样进行临界访问或互斥访问的: 设信号量值为1, 当一个进程1运行是,使用资源,进行P操作,即对信号量值减1,也就是资源数少了1个。这是信号量值为0。系统中规定当信号量值为0是,必须等待,知道信号量值不为零才能继续操作。 这时如果进程2想要运行,那么也必须进行P操作,但是此时信号量为0,所以无法减1,即不能P操作,也就阻塞。这样就到到了进程1排他访问。 当进程1运行结束后,释放资源,进行V操作。资源数重新加1,这是信号量的值变为1. 这时进程2发现资源数不为0,信号量能进行P操作了,立即执行P操作。信号量值又变为0.次数进程2咱有资源,排他访问资源。 这就是信号量来控制互斥的原理

作者:纸简书生
链接:http://www.jianshu.com/p/04ca5470f212
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

信号量使用

写在最后-多线程不好的地方

  • 使用不好,会造成死锁问题

    最经典的例子:


    死锁问题

    问题分析:首先touchBegan方法肯定是在主线程执行的。然后我们是在方法里面,进行了一个同步的函数任务,因为是同步任务,那么主线程就要等这个任务结束了,才往后面走。但是这个任务是会被主队列加到主线程里执行,然而这个时候主线程里正在执行touchBegan这个任务,只有等这个任务结束了,才能执行其他的。因此双方就都在等待,造成死锁问题。

    解决方法很简单:既然是双方相互等待造成的问题,那只要让一方不等就行了。这个任务可以执行在其他线程就行了或者不使用同步函数,使用异步函数

  • 多线程使用,肯定会有线程安全问题(访问临界资源)

    这个就不过多说了,类似的问题很多,比如买票什么的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335