美文网首页
七、大中枢派发

七、大中枢派发

作者: faterman | 来源:发表于2018-01-25 16:45 被阅读4次

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);
        });
    }

相关文章

网友评论

      本文标题:七、大中枢派发

      本文链接:https://www.haomeiwen.com/subject/fmevaxtx.html