iOS多线程之GCD

多线程相关概念

进程与线程

进程概念: 进程是程序在计算机上的一次执行活动,打开一个app,就开启了一个进程,可包含多个线程。
线程概念: 独立执行的代码段,一个线程同时间只能执行一个任务,反之多线程并发就可以在同一时间执行多个任务。
iOS程序中,主线程(又叫作UI线程)主要任务是处理UI事件,显示和刷新UI,(只有主线程有直接修改UI的能力)耗时的操作放在子线程(又叫作后台线程、异步线程)。在iOS中开子线程去处理耗时的操作,可以有效提高程序的执行效率,提高资源利用率。但是开启线程会占用一定的内存,(主线程的堆栈大小是1M,第二个线程开始都是512KB,并且该值不能通过编译器开关或线程API函数来更改)降低程序的性能。所以一般不要同时开很多线程。

线程相关

同步线程:同步线程会阻塞当前线程去执行线程内的任务,执行完之后才会反回当前线程。
异步线程:异步线程不会阻塞当前线程,会开启其他线程去执行线程内的任务。
串行队列:线程任务按先后顺序逐个执行(需要等待队列里面前面的任务执行完之后再执行新的任务)。
并发队列:多个任务按添加顺序一起开始执行(不用等待前面的任务执行完再执行新的任务),但是添加间隔往往忽略不计,所以看着像是一起执行的。
并发VS并行:并行是基于多核设备的,并行一定是并发,并发不一定是并行。

多线程中会出现的问题

Critical Section(临界代码段)
指的是不能同时被两个线程访问的代码段,比如一个变量,被并发进程访问后可能会改变变量值,造成数据污染(数据共享问题)。
Race Condition (竞态条件)
当多个线程同时访问共享的数据时,会发生争用情形,第一个线程读取改变了一个变量的值,第二个线程也读取改变了这个变量的值,两个线程同时操作了该变量,此时他们会发生竞争来看哪个线程会最后写入这个变量,最后被写入的值将会被保留下来。
Deadlock (死锁)
两个(多个)线程都要等待对方完成某个操作才能进行下一步,这时就会发生死锁。
Thread Safe(线程安全)
一段线程安全的代码(对象),可以同时被多个线程或并发的任务调度,不会产生问题,非线程安全的只能按次序被访问。
所有Mutable对象都是非线程安全的,所有Immutable对象都是线程安全的,使用Mutable对象,一定要用同步锁来同步访问(@synchronized)。
互斥锁:能够防止多线程抢夺造成的数据安全问题,但是需要消耗大量的资源
原子属性(atomic)加锁
atomic: 原子属性,为setter方法加锁,将属性以atomic的形式来声明,该属性变量就能支持互斥锁了。
nonatomic: 非原子属性,不会为setter方法加锁,声明为该属性的变量,客户端应尽量避免多线程争夺同一资源。
Context Switch (上下文切换)
当一个进程中有多个线程来回切换时,context switch用来记录执行状态,这样的进程和一般的多线程进程没有太大差别,但会产生一些额外的开销。

多线程编程技术的优缺点比较

NSThread (抽象层次:低)
优点:轻量级,简单易用,可以直接操作线程对象
缺点: 需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。
Cocoa NSOperation (抽象层次:中)
优点:不需要关心线程管理,数据同步的事情,可以把精力放在学要执行的操作上。基于GCD,是对GCD 的封装,比GCD更加面向对象
缺点: NSOperation是个抽象类,使用它必须使用它的子类,可以实现它或者使用它定义好的两个子类NSInvocationOperation、NSBlockOperation.
GCD 全称Grand Center Dispatch (抽象层次:高)

优点:是 Apple 开发的一个多核编程的解决方法,简单易用,效率高,速度快,基于C语言,更底层更高效,并且不是Cocoa框架的一部分,自动管理线程生命周期(创建线程、调度任务、销毁线程)。
缺点: 使用GCD的场景如果很复杂,就有非常大的可能遇到死锁问题。

GCD抽象层次最高,使用也简单,因此,苹果也推荐使用GCD

GCD中的队列类型

GCD编程的核心就是dispatch队列,dispatch block的执行最终都会放进某个队列中去进行。

The main queue(主线程串行队列): 与主线程功能相同,提交至Main queue的任务会在主线程中执行,
Main queue 可以通过dispatch_get_main_queue()来获取。
Global queue(全局并发队列): 全局并发队列由整个进程共享,有高、中(默认)、低、后台四个优先级别。
Global queue 可以通过调用dispatch_get_global_queue函数来获取(可以设置优先级)
Custom queue (自定义队列): 可以为串行,也可以为并发。
Custom queue 可以通过dispatch_queue_create()来获取;
Group queue (队列组):将多线程进行分组,最大的好处是可获知所有线程的完成情况。
Group queue 可以通过调用dispatch_group_create()来获取,通过dispatch_group_notify,可以直接监听组里所有线程完成情况。

重点来了哈

GCD在项目中的实际应用

GCD在项目中的实际应用

GCD在项目中的实际应用(一定要说够三遍!!!)

以下请求以afn为例

场景一

个页面多个网络请求,并且多个请求同时执行,都执行完成以后进行数据操作
实现方式1:dipatch_group

AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
    dispatch_group_enter(self.group);
    [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
        dispatch_group_leave(self.group);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        dispatch_group_leave(self.group);
    }];

    dispatch_group_enter(self.group);
    [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
        dispatch_group_leave(self.group);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        dispatch_group_leave(self.group);
    }];
    dispatch_group_notify(self.group, self.chquue, ^{
        NSLog(@"11111");
    });

其他类似代码实现相同效果

dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t conQueue = dispatch_queue_create("fsfsdfsf2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t seQueue = dispatch_queue_create("asdsadasdasda", NULL);
    dispatch_queue_t seQueue1 = dispatch_queue_create("asdsadasasdadassda", NULL);
//
//
    
    dispatch_group_enter(group);
    dispatch_async(conQueue, ^{
        sleep(6);
        NSLog(@"111111");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(seQueue, ^{
        sleep(2);
        NSLog(@"222222");
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(seQueue1, ^{
        sleep(4);
        NSLog(@"33333");
        dispatch_group_leave(group);
    });
//
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"444444");
    });
//
    NSLog(@"hhahahaha");
其中的队列可以是不同的队列,只要group是同一个,都会得到最后执行notify代码的效果,如果是同一个串行队列,则会按照1-2-3-4的顺序串行执行以上4个任务

实现方式2:dispatch_semaphore

dispatch_async(self.chquue, ^{
        dispatch_semaphore_t SEM = dispatch_semaphore_create(0);
        AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
        
        [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            dispatch_semaphore_signal(SEM);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            dispatch_semaphore_signal(SEM);
        }];
        
        dispatch_semaphore_t SEM2 = dispatch_semaphore_create(0);
        [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            dispatch_semaphore_signal(SEM2);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            dispatch_semaphore_signal(SEM2);
        }];
        dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(SEM2, DISPATCH_TIME_FOREVER);
        
        NSLog(@"hahaha");
    });
    NSLog(@"hello");
此处为什么要套一个dispatch_ASYNC呢?这样做是为了防止阻塞主线程的任务执行!
场景二

一个页面多个网络请求,并且多个请求串执行,下一个请求需要等待上一个的请求结果才能继续请求
实现方式1:dipatch_group,如何使用group的方式来实现这个需求,欢迎大神提出意见
实现方式2:dispatch_semaphore

dispatch_async(self.chquue, ^{
        dispatch_semaphore_t SEM = dispatch_semaphore_create(0);
        AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
        
        [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            dispatch_semaphore_signal(SEM);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            dispatch_semaphore_signal(SEM);
        }];
        
       dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
        [MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            dispatch_semaphore_signal(SEM);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            dispatch_semaphore_signal(SEM);
        }];
        dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
        
        NSLog(@"hahaha");
    });
    NSLog(@"hello");
这段代码的关键点是只使用一个sem来实现串行执行异步任务

当然如果哪位大神有更好的实现方式欢迎指点!!

场景三

死锁

dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
  dispatch_sync(serialQueue, ^{
      NSLog(@"会执行的代码");
      //当嵌套的这个任务是同步且在串行队列中执行时,就会造成死锁
      dispatch_sync(serialQueue, ^{
          NSLog(@"代码不执行");
      });
  });

关于dispatc_group

dispatch_enter和dispatch_leave要成对出现,否则奔溃。

补充

关于dispatch_set_target_queue
场景一

使用dispatch_set_target_queue更改Dispatch Queue的执行优先级
dispatch_queue_create函数生成的DisPatch Queue不管是Serial DisPatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数

- (void)testTeagerQueue1 {
2     dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL);
3     dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
4     
5     dispatch_set_target_queue(serialQueue, globalQueue);
6     // 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。
7 }
场景二

使用dispatch_set_target_queue将多个串行的queue指定到了同一目标,那么着多个串行queue在目标queue上就是同步执行的,不再是并行执行。

- (void)testTargetQueue {
  //1.创建目标队列
  dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
  
  //2.创建3个串行队列
  dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
  dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
  dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
  
  //3.将3个串行队列分别添加到目标队列
  dispatch_set_target_queue(queue1, targetQueue);
  dispatch_set_target_queue(queue2, targetQueue);
  dispatch_set_target_queue(queue3, targetQueue);
  
  
  dispatch_async(queue1, ^{
      NSLog(@"1 in");
      [NSThread sleepForTimeInterval:3.f];
      NSLog(@"1 out");
  });
  
  dispatch_async(queue2, ^{
      NSLog(@"2 in");
      [NSThread sleepForTimeInterval:2.f];
      NSLog(@"2 out");
  });
  dispatch_async(queue3, ^{
      NSLog(@"3 in");
      [NSThread sleepForTimeInterval:1.f];
      NSLog(@"3 out");
  });
}

以上是我在工作中遇到的关于gcd的一些总结,欢迎各路大神补充,共同进步!!如果您看过文章觉得用得上,请给个赞谢谢!

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

推荐阅读更多精彩内容

  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 359评论 0 0
  • 多线程 在iOS开发中为提高程序的运行效率会将比较耗时的操作放在子线程中执行,iOS系统进程默认启动一个主线程,用...
    郭豪豪阅读 2,586评论 0 4
  • GCD (Grand Central Dispatch) :iOS4 开始引入,使用更加方便,程序员只需要将任务添...
    池鹏程阅读 1,323评论 0 2
  • iOS多线程之GCD 什么是GCD GCD(grand central dispatch) 是 libdispat...
    comst阅读 1,196评论 0 0
  • 原创文章 转载请注明出处, 谢谢! (~ o ~)Y 本文思维导图 GCD是什么 全称是 Grand Centra...
    Jimmy_P阅读 4,659评论 10 67