前文回顾:

      上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue;第二种是直接使用系统提供的标准Dispatch Queue :Main Dispatch Queue和Global Dispatch Queue,具体的实现方式请参照上篇博客《iOS多线程开发之GCD(上篇)》。

 

这篇博客主要讲解以下苹果提供的一些常用GCD和代码示例及其注意点。

  • dispatch_set_target_queue

  • dispatch_after

  • dispatch_once / dispatch_apply

  • Dispatch Group

  • dispatch_barrier_sync

  • dispatch_suspend / dispatch_resume

  • Dispatch Semaphore

一、dispatch_set_target_queue 

        dispatch_set_target_queue中涉及的代码示例来源于网络

        1、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函数。

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

dispatch_queue_t serialQueue = dispatch_queue_create("com.beckwang.www",NULL);

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0); 
// 第一个参数为要变更优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。dispatch_set_target_queue(serialQueue, globalQueue);

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

       2、dispatch_set_target_queue作为执行阶层,修改队列的目标队列使多个serial queue在目标queue上一次只有一个执行

       万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

      第一种情况:使用dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2)实现队列的动态调度管理

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训 View Code

      第二种情况:使用dispatch_set_target_queue将多个串行的queue指定到了同一目标,那么着多个串行queue在目标queue上就是一次只能执行一个(化并行为串行)。

        适用场景:一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。这时候dispatch_set_target_queue将起到作用。

      (1)没有使用dispatch_set_target_queue时:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

- (void)testTargetQueue3 {    //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");
    });
}

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

      打印结果:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

2017-07-04 19:52:51.915 Test[5759:927698] 1 in2017-07-04 19:52:51.915 Test[5759:927699] 2 in2017-07-04 19:52:51.915 Test[5759:927701] 3 in2017-07-04 19:52:52.916 Test[5759:927701] 3 out2017-07-04 19:52:53.921 Test[5759:927699] 2 out2017-07-04 19:52:54.919 Test[5759:927698] 1 out

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

     结论:多个串行queue之间是并行的!

   (2)使用dispatch_set_target_queue设置target

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

- (void)testTargetQueue3 {    //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");
    });
}

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

      打印结果:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

2017-07-04 19:58:33.667 Test[5830:968024] 1 in2017-07-04 19:58:36.672 Test[5830:968024] 1 out2017-07-04 19:58:36.672 Test[5830:968024] 2 in2017-07-04 19:58:38.678 Test[5830:968024] 2 out2017-07-04 19:58:38.679 Test[5830:968024] 3 in2017-07-04 19:58:39.683 Test[5830:968024] 3 out

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

     结论:多个串行queue之间是串行的!

 

二、dispatch_after

     如果需要延时处理某件事情,我们可以使用dispatch_after,需要注意的是dispatch_after并不是将任务追加到队列dispatch_queue后再根据时间参数延迟执行block代码,而是在指定时间后追加任务到到dispatch_queue。代码示例:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, *^dispatch_get_main_queue ---> diapatch_get_gloab_queue 就可以更改执行线程

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

     实现延时处理除了上面的GCD(dispatch_after)外,还可以通过以下方法:

     (1)performSelector(NSObject)方法:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

// 不带参数[self performSelector:@selector(doSomething) withObject:self afterDelay:3.0f];// 带参数[self performSelector:@selector(delayDo:) withObject:@"paramtest" afterDelay:3.0f];// 取消全部[NSObject cancelPreviousPerformRequestsWithTarget:self];// 取消不传参的方法[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayDo:) object:nil];// 取消传参的方法[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayDo:) object:@"paramtest"];

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

    (2)NSTimer的类方法:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

// 不带参数[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(doSomething) userInfo:nil repeats:NO];// 带参数[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(doSomething) userInfo:@"paramtest" repeats:NO];

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

       使用NSTimer时注意事项可以参考我的另外一篇博客《实现定时器NSTimer的高逼格方法

     (3)sleep(NSThreed)

[NSThread sleepForTimeInterval:1.0f];// 这里执行延迟处理代码[self doSomething];

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

 

三、dispatch_once 和 dispacth_apply

     dispatch_once整个app运行周期内只执行一次代码,多用于单例模式。

dispatch_once_t *predicate:一个全局的变量   dispatch_block_t block:block函数块

     dispatch_apply让指定代码按设定次数多次执行,dispatch_apply类似一个for循环,会在指定的dispatch queue中运行block任务n次,如果队列是并发队列,则会并发执行block任务,如果队列是串行队列,则block任务只会同步执行,但是dispatch_apply是一个同步调用,block任务执行n次后才返回。

size_t iterations:执行次数  dispatch_queue_t queue:队列   void (^block)(size_t):block函数块

   代码示例:

 (1)dispatch_once 

   自定义block函数块

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

//定义blocktypedef void (^BLOCK)(void);    
//将执行代码封装到block中BLOCK myBlock = ^(){    static int count = 0;
    NSLog(@"count=%d",count++);
};

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

  执行

// 只会执行一次static dispatch_once_t predicate;
dispatch_once(&predicate, myBlock);

 打印结果:count  = 0;

 

  (2) dispatch_apply

  自定义block

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

//定义blocktypedef void (^BLOCK)(size_t);    
//将函数封装到blockBLOCK myBlock = ^(size_t size){    static int count = 0;
    NSLog(@"count=%d",count++);      
};

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

  执行

dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), myBlock);

  打印结果:

count = 0count = 2count = 3count = 1count = 4

    显而易见,如果dispatch_apply的队列是自定义的串行队列

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);

    输出结果将是:

count = 0count = 1count = 2count = 3count = 4

   dispatch_apply 可以处理一个任务重复执行次数量级比较大的应用场景,假设从服务器上获取一组数组数据(超过100个元素对象)然后进行字典转化模型

   多线程并发处理:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

// 多线程并发处理,可能造成线程爆炸及死锁dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i < 999; i++){
   dispatch_async(queue, ^{         // 字典转模型    });
}
dispatch_barrier_sync(dispatch_get_main_queue(), ^{
       NSLog(@"主线程更新");
});---------------------------这里是分割线---------------------------// dispatch_apply 方式 (优先选择)NSArray *dictArray = nil;

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
   
 dispatch_async(queue, ^{

        dispatch_apply(dictArray.count, queue,  ^(size_t index){            //字典转模型
        });

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"主线程更新");
        });
    });

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

 

四、Dispatch Group

         在追加到Dispatch Queue中的多个任务处理全部完毕之后想执行结束处理。如果只是使用一个Serial Dispatch Queue(串行队列)时,只要将想执行的处理全部追加到该串行队列中并在最后追加结束处理即可,但是在使用Concurrent Queue 时,可能会同时使用多个Dispatch Queue时,这就需要使用Dispatch Group。

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

- (void)testDispatchGroup{
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcdgroup.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{        for (int i = 0; i < 10; i++) {            if (i == 9) {
                NSLog(@"test001");
            }
        }
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"test002");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"test003");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"全部完成");
    });
}

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

     打印结果:

2017-07-06 23:30:57.449 Test[8724:1743565] test0022017-07-06 23:30:57.449 Test[8724:1743547] test0032017-07-06 23:30:57.449 Test[8724:1743549] test0012017-07-06 23:30:57.449 Test[8724:1743547] 全部完成

    Dispatch Group广泛运用到异步获取网络数据最后汇总的情况,如异步获取多张网络图片资源后拼接成一张图片等等。

 

五、dispatch_barrier_async

    在访问数据库和文件时,如前所述,使用Serial Dispatch Queue可避免数据资源的竞争问题。众所周知,写处理与写处理,写处理与读处理会发生数据一致性或数据竞争问题,但是读处理与读处理之前不存在数据一致性问题,为了提高效率我们可以这样设想:读处理可以追加到Concurrent Dispatch Queue(并发队列)中,而写处理在任意一个没有读取处理执行的状态下追加到Serial Dispatch Queue(串行队列)中(在写处理结束之前,读处理不可执行)。

代码示例:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

- (void)testDispatchBarrier{
    
    dispatch_queue_t queue = dispatch_queue_create("com.gcdbarrier.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        NSLog(@"block001_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block002_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block003_read");
        
    });
    
    dispatch_barrier_sync(queue, ^{
        
        NSLog(@"block004_write");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block005_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block006_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block007_read");
    });
    
}

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

   打印结果:

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训

2017-07-06 23:59:27.936 Test[9080:1907162] block003_read2017-07-06 23:59:27.936 Test[9080:1907194] block002_read2017-07-06 23:59:27.936 Test[9080:1907163] block001_read2017-07-06 23:59:27.937 Test[9080:1907028] block004_write2017-07-06 23:59:27.937 Test[9080:1907163] block005_read2017-07-06 23:59:27.937 Test[9080:1907162] block007_read2017-07-06 23:59:27.937 Test[9080:1907194] block006_read

万码学堂,电脑培训,计算机培训,Java培训,JavaEE开发培训,青岛软件培训,软件工程师培训


http://www.cnblogs.com/beckwang0912/p/7112121.html