多线程之GCD

简介

GCD:(Grand Central Dispatch)GCD是基于C语言的。
GCD本身是苹果公司为多核的并行运算提出的解决方案,它完全由系统管理线程,我们不需要编写线程代码,只需要在适当的调度队列中(dispatch queue)实现要执行的任务就好。GCD会负责创建线程和调度你的任务,系统直接提供线程管理。
GCD能推迟昂贵的计算任务,并在后台运行,从而可以改善工程的性能。
GCD能提供易于我们使用的并发模型从而帮我们避开并发陷阱。

调度队列(dispath queue)

queue 是GCD的一个重要概念,我们可以这样理解:我们要执行一个长期任务,可以将这个任务分成多个工作单元,并将这些工作单元添加到dispath queue 中,系统则会为我们管理这些工作单元,,为我们在多个线程上执行工作单元,我们不需要直接启动和管理后台线程。
GCD的dispach queue 是严格按照先进先出的的原则工作的,添加到dispath queue的工作单元将始终按照加入dispath queue的顺序启动。

一、并行队列(concurrent dispath queue)
  • 并发是可以同时执行多个任务,实际上也是按照先进先出(FIFO)的原则执行的,并发会在上一个任务即将执行完之前就出列下一个任务并执行。
  • 并发的任务数量,会根据应用和系统动态变化,例如:可用的核数量、其他进程正在执行的工作量、其他串行的工作量等等。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
DISPATCH_QUEUE_PRIORITY_HIGH 2      优先级最高
DISPATCH_QUEUE_PRIORITY_DEFAULT 0   优先级默认
DISPATCH_QUEUE_PRIORITY_LOW (-2)    优先级最低

二、串行队列(serial dispatch queue)

  • 应用任务按顺序执行,串行queue每次只执行一个任务,可替代锁,和锁不一样的是,串行queue确保任务按可预测的顺序执行。并且不容易产生死锁。
dispatch_queue_t queue = dispatch_queue_create("cn.test.queue", NULL);
第一个参数是queue的名字
第二个参数是一组queue的属性

三、同步&异步

  • 同步:函数执行玩这个任务才能执行下一个任务。对于GCD而言:同步函数执行一个任务,直到执行完成它的预定任务后才返回。
  • 异步:函数不会等到任务完成,它会立即执行下一个任务,同时当前任务会继续在后台执行,对于GCD而言:与同步相反,它会立即返回,而任务继续执行,它并不会等待任务完成再返回。
    NSLog(@"当前线程:%@", [NSThread currentThread]);
    //创建一个并发线程
    dispatch_queue_t queue;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    //创建一个异步任务
    dispatch_async(queue, ^{
        NSLog(@"异步任务,当前线程:%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"同步任务,当前线程:%@",[NSThread currentThread]);
    });
打印消息:
当前线程:<NSThread: 0x7fb102508120>{number = 1, name = main}
异步任务,当前线程:<NSThread: 0x7fb102782970>{number = 2, name = (null)}
同步任务,当前线程:<NSThread: 0x7fb102508120>{number = 1, name = main}

四、循环迭代(dispatch_apply)

  • 如果每次迭代与其他迭代独立无关,,而且循环迭代执行顺序也无关紧要的话,我们可以尝试使用dispath_applydispatch_apply_f代替for循环。
    这两个函数可以同时并发多个循环迭代,这样可以大大提高效率。
  • dispatch_applydispatch_apply_f时你可以指定串行或并发 queue。并发queue允许同时执行多个循环迭代,而串行queue就没太大必要使用了。
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    //你传递的block必须包含一个size_t类型的参数,用来标识当前循环迭代。第一次迭代这个参数值为0,最后一次值为count - 1
    size_t count = 8;
    dispatch_apply(count, queue, ^(size_t t) {
        NSLog(@"%zu",t);
    });
打印结果:
2016-07-13 11:10:33.423 GCDTest[1867:83704] 0
2016-07-13 11:10:33.423 GCDTest[1867:83703] 2
2016-07-13 11:10:33.423 GCDTest[1867:83662] 1
2016-07-13 11:10:33.423 GCDTest[1867:83714] 3
2016-07-13 11:10:33.423 GCDTest[1867:83704] 4
2016-07-13 11:10:33.424 GCDTest[1867:83703] 5
2016-07-13 11:10:33.424 GCDTest[1867:83662] 6
2016-07-13 11:10:33.424 GCDTest[1867:83714] 7

由此可见,这些迭代是并发执行的,并且顺序不固定,

五、暂停和继续queue

  • 我们可以使用dispatch_suspend函数暂停一个queue以阻止它执行block对象;使用dispatch_resume函数继续dispatch queue。
  • 挂起一个queue不会导致正在执行的block停止,只会导致不再继续执行还未执行的任务。dispatch_resume会唤醒已挂起的dispatch queue。
  • 调用dispatch_suspend会增加queue的引用计数,调用dispatch_resume则减少queue的引用计数。
  • 当引用计数大于0时,queue就保持挂起状态。因此你必须对应地调用suspend和resume函数(必须成对出现)。
  • 挂起和继续是异步的,而且只在执行block之间(比如在执行一个新的block之前或之后)生效。
    NSLog(@"当前线程:%@", [NSThread currentThread]);
    //创建一个串发线程
    dispatch_queue_t queue;
    queue = dispatch_queue_create("cn.test.queue", NULL);
    dispatch_async(queue, ^{
        NSLog(@"异步任务,当前线程:%@",[NSThread currentThread]);
    });
    //挂起queue
    dispatch_suspend(queue);
    dispatch_sync(queue, ^{
        NSLog(@"同步任务,当前线程:%@",[NSThread currentThread]);
    });
    //结束挂起
    dispatch_resume(queue);
打印结果
当前线程:<NSThread: 0x7fdb53d00550>{number = 1, name = main}

六、常用

//  后台执行:
 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });
 // 主线程执行:
 dispatch_async(dispatch_get_main_queue(), ^{
      // something
 });

 // 一次性执行:
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
     // code to be executed once
 });

 // 延迟2秒执行:
 double delayInSeconds = 2.0;
 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
     // code to be executed on the main queue after delay
 });

 // 自定义dispatch_queue_t
 dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
 dispatch_async(urls_queue, ^{  
   // your code 
 });
// 销毁队列 
 dispatch_release(urls_queue);

 // 合并汇总结果
 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程一
 });
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程二
 });
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
      // 汇总结果
 });
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
将需要执行的任务对象指定到不同的队列中去处理,比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:

七、一些概念

  • 串行&并行: 任务串行执行就是每次只有一个任务被执行,任务并发执行就是在同一时间可以有多个任务被执行。
  • 临界区: 就是一段代码不能被并发执行,也就是,两个线程不能同时执行这段代码。这很常见,因为代码去操作一个共享资源,例如一个变量若能被并发进程访问,那么它的值将不再可信。
  • 竞态条件: 这种状况是指基于特定序列或时机的事件的软件系统以不受控制的方式运行的行为,例如程序的并发任务执行的确切顺序。竞态条件可导致无法预测的行为,而不能通过代码检查立即发现。
  • 死锁:所谓的死锁是指两个线程它们都卡住了,并等待对方完成或执行其它操作。第一个不能完成是因为它在等待第二个的完成。但第二个也不能完成,因为它在等待第一个的完成。
  • 线程安全: 线程安全是代码能在多线程或并发任务中被安全的调用,而不会导致任何问题(数据损坏,崩溃,等)。线程不安全的代码在某个时刻只能在一个环境中运行。一个线程安全代码的例子是 NSDictionary 。你可以在同一时间在多个线程中使用它而不会有问题。另一方面,NSMutableDictionary 就不是线程安全的,应该保证一次只能有一个线程访问它。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容

  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 359评论 0 0
  • 多线程 在iOS开发中为提高程序的运行效率会将比较耗时的操作放在子线程中执行,iOS系统进程默认启动一个主线程,用...
    郭豪豪阅读 2,587评论 0 4
  • GCD (Grand Central Dispatch) :iOS4 开始引入,使用更加方便,程序员只需要将任务添...
    池鹏程阅读 1,323评论 0 2
  • iOS多线程之GCD 什么是GCD GCD(grand central dispatch) 是 libdispat...
    comst阅读 1,196评论 0 0
  • 本文由作者冷爱所写,欢迎分享到朋友圈。如需转载,请与我们联系并获得作者本人许可,任何未获得作者授权的转载均为侵权。...
    冷爱阅读 215评论 0 0