七、大中枢派发

GCD即Grand Central Dispatch,它是异步执行任务的技术之一。一般将应用程序中技术的线程管理用的代码在系统层级上实现。开发者只需要将定义好的任务追加到dispatch queue中,GCD就能生成必要的线程并按计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,相较于以前的多线程管理更加有效率。

实例:

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create(NULL, NULL);  
          
dispatch_async(mySerialDispatchQueue, ^{  
   //执行长时间的处理  
    //图像识别  
    //数据库读取  
    dispatch_async(dispatch_get_main_queue(), ^{  
        //用户界面更新  
    });   
});

API Dispatch Queue

  1. 生成dispatch queue的API,生成Serial Dispatch Queue(一个线程,追加的任务按照追加顺序一次执行)
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.faterman.gcd_demo_mySerialDispatchQueue", NULL);  

  1. 生成Concurrent Dispatch Queue(由XNU内核启动合理数量的线程,并发执行追加的任务)
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.faterman.gcd_demo_mySerialDispatchQueue", DISPATCH_QUEUE_CONCURRENT);  

Main Dispatch Queue/Global Dispatch Queue

Main Dispatch Queue:
这是在主线程中执行的Dispatch Queue,是一个Serial Dispatch Queue。追加到这个Dispatch Queue中的任务在主线程的Run Loop中执行。所以用户界面更新的操作必须在 Main Dispatch Queue 中进行。

/* 
    Main Dispatch Queue 的获取方法 
 */  
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();  

Global Dispatch Queue:
这是一个Concurrent Dispatch Queue,开发中一般没有必要通过生成的方式,直接采用Global Dispatch Queue就可以。
Global Dispatch Queue包括四个优先级:High Priority,Default Priority,Low Priority,BackGround Priority。他们通过XNU内核的管理用于Global Dispatch Queue的线程。但是优先级的确认只是一个大致的判断,开发中可根据任务的重要性,选择追加,但是不能精确控制。

/* 
           Global Dispatch Queue 的获取方法 
        */  
       dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);  
         
       dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
         
       dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);  
         
       dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

API dispatch_set_target_queue

作用1:改变Disaptch Queue的优先级。该方法可用于变更自己创建的Dispatch Queue的优先级。

dispatch_queue_t myDispatchQueue = dispatch_queue_create("com.faterman.gcd_demo.myDispatchQueue", NULL);  
        dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);  
          
        dispatch_set_target_queue(myDispatchQueue, globalDispatchQueue);  
/*
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue)

其中object为自己建立待修改的dispatch queue,queue为参照queue。

因为Main Dispatch Queue 和Global Dispatch Queue均为全局的,所以不可作为第一个参数,如果作为了,不能预知会出现什么结果。
*/

作用2:作为Disaptch Queue的执行阶层。在必将不可并行执行的处理追加到多个Serial Dispatch Queue中的时候,如果使用dispatch_set_target_queue可防止并行处理。参考

dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);//目标队列
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);//串行队列
    dispatch_queue_t queue2 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);//并发队列
    //设置参考
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
 
    dispatch_async(queue2, ^{
        NSLog(@"job3 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"job3 out");
    });
    dispatch_async(queue2, ^{
        NSLog(@"job2 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"job2 out");
    });
    dispatch_async(queue1, ^{
        NSLog(@"job1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"job1 out");
    });

dispatch_after

/*这两个宏本身就是dispatch_time_t类型*/
DISPATCH_TIME_NOW //表示当前;
DISPATCH_TIME_FOREVER //表示遥远的未来

// 第一个参数是一个dispatch_time_t,第二个参数是纳秒
dispatch_time(dispatch_time_t when, int64_t delta);

// 有关时间的宏
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull

NSEC_PER_SEC,每秒有多少纳秒。
USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上)
NSEC_PER_USEC,每毫秒有多少纳秒。

// 所以,延时1秒可以写成如下几种:
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

// 三秒后提交一个任务,注意是提交,不一定是执行
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"waited at least three seconds");
    });

// 生成精确时间的dispatch_time_t,比如追加到2020.01.01
dispatch_time_t getDispatchTimeByDate(NSDate *date) {
    NSTimeInterval interval;
    double second, subsecond;
    struct timespec time;
    dispatch_time_t milestone;
    
    interval = [date timeIntervalSince1970];
    subsecond = modf(interval, &interval);
    time.tv_sec = second;
    time.tv_nsec = subsecond *NSEC_PER_SEC;
    milestone = dispatch_walltime(&time, 0);
    return milestone;
}

Dispatch Group

先看一段代码:

dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  
dispatch_async(globalDispatchQueue, ^{  
    NSLog(@"1");  
});  
dispatch_async(globalDispatchQueue, ^{  
    NSLog(@"2");  
});  
dispatch_async(globalDispatchQueue, ^{  
    NSLog(@"3");  
}); 

平时当我这么追加的的时候,因为是concurrent dispatch queue,所以不能确定执行顺序,如果需要在全部执行结束之后进行一些操作的话,实现起来比较麻烦。这时候就用到了Dispatch Group。

dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
         
       dispatch_group_t group = dispatch_group_create();  
         
       dispatch_group_async(group, globalDispatchQueue, ^{  
           NSLog(@"1");  
       });  
       dispatch_group_async(group, globalDispatchQueue, ^{  
           NSLog(@"2");  
       });  
       dispatch_group_async(group, globalDispatchQueue, ^{  
           NSLog(@"3");  
             
       });  
         
       dispatch_group_notify(group, globalDispatchQueue, ^{  
           NSLog(@"执行完毕");  
       }); 

执行结果:

2015-03-17 15:12:19.698 GCD_Demo[872:1203] 1  
2015-03-17 15:12:19.698 GCD_Demo[872:1403] 3  
2015-03-17 15:12:19.698 GCD_Demo[872:1303] 2  
2015-03-17 15:12:19.701 GCD_Demo[872:1303] 执行完毕  

dispatch_barrier_async

有这么一种情况,你的程序目录下有一个文件,在多线程编程环境下,需要读取和写入。

  1. 并发着读写?
    这样读的时候也许在写,那么可能读到与内容不符的数据,可能因为非法访问导致程序异常终止。
  2. serial读写?
    浪费资源,效率低下。
  3. serail写,并发读,并且两者之前有区分。
    可以自己通过group,设置优先级来实现。繁琐。
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t concurrentQueue = dispatch_queue_create("faterman.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(concurrentQueue, ^(){
        [self readFile:@"dispatch-1"];
    });
    dispatch_async(concurrentQueue, ^(){
        [self readFile:@"dispatch-2"];
    });
    
    dispatch_barrier_async(concurrentQueue, ^{
        [self writeToFile:@"saleTask"];
    });
    
    dispatch_async(concurrentQueue, ^(){
        [self readFile:@"dispatch-3"];
    });
    dispatch_async(concurrentQueue, ^(){
        [self readFile:@"dispatch-4"];
    });
    
}

- (void)readFile:(NSString *)taskName {
    NSLog(@"read file in task %@",taskName);
}

- (void)writeToFile:(NSString *)taskName {
    NSLog(@"write filet in task %@",taskName);
}

dispatch_semaphore

信号量其实就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。

其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
 
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
 
//提高信号量
dispatch_semaphore_signal(信号量)

实际问题:

假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?或者我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定cpu吃不消,那么我们这里也可以用信号量控制一下最大开辟线程数。

-(void)dispatchSignal{
    //crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);   
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     
    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);       
    });<br>
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);       
    });<br>
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);       
    });   
}

设置信号量初始化值为1,那么就是只能有一个线程持有,此时其实就是个锁了。

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

推荐阅读更多精彩内容