iOS多线程开发—GCD(二)

前言

在上一篇文章iOS多线程开发—GCD(二)中我介绍了什么是多线程,GCD与线程的关系,以及如何建立队列和简单的使用,在这篇文章中我讲重点总结一下GCD一些高级用法。编程

建立dispatch_queue_t须要注意的问题

咱们能够经过dispatch_get_global_queue(long identifier, unsigned long flags);获取全局队列,经过dispatch_get_main_queue();获取主队列,经过dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);建立并行或者串行队列。 咱们须要注意的是,dispatch_get_global_queue(long identifier, unsigned long flags);dispatch_get_main_queue();这两种方法都是让咱们获取系统已经建立好的队列,所以咱们不须要对这些队列进行内存管理,系统会帮助咱们在合适的时间释放这些队列。安全

  • 在《Objective-C 高级编程》这本书中,做者写到当咱们使用dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);去建立队列的时候,咱们须要手动对这个队列进行内存管理,虽然咱们使用了ARC,可是dispatch_queue_t并非一个对象类型,所以ARC没法对它进行内存管理这时候咱们就须要使用dispatch_release(queue)去释放这个队列。可是根据我本身的实践和查阅苹果官方文档显示,若是咱们使用了ARC,那么咱们是不须要使用dispatch_release和dispatch_retain去管理队列的,事实上在ARC中使用dispatch_release和dispatch_retain会报错。

GCD API使用

1. dispatch_queue_create

dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr); 这个方法可让咱们建立一个队列,第一个参数是队列的名字,也能够为空,可是不利于debug。第二个参数是队列的类型,若是写NULL,那么默认是串行队列,DISPATCH_QUEUE_CONCURRENT将建立并行队列。bash

//串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
复制代码

理论上咱们能够在这里建立任意多个队列,而且多个串行队列是能够并行执行的。可是当咱们使用了过多的串行队列时,咱们会建立相应的串行线程,过多的线程建立将消耗系统资源。多线程

2. Main Dispatch Queue / Global Dispatch Queue

//获取主队列,在主线程上执行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//获取默认优先级的全局队列,并行执行
dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
复制代码

dispatch_get_global_queue(long identifier, unsigned long flags);第一个参数指定队列的优先级,第二个参数是flag,大多时间使用0。 优先级分为四级,从高到低依次为,高,默认,低,后台app

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
复制代码
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(concurrentQueue1, ^{
//同步任务追加到并行队列中
        NSLog(@"Concurrent Queue");
        NSLog(@"Concurrent Queue%@",[NSThread currentThread]);
        dispatch_async(mainQueue, ^{
        //异步任务追加到主队列中
            NSLog(@"Main Queue");
            NSLog(@"Main Queue%@",[NSThread currentThread]);
        });
    });
    NSLog(@"Done");
    NSLog(@"Done%@",[NSThread currentThread]);
复制代码

上面这个使用,让咱们能够把耗时操做放到并行队列中,而且将结果放到主队列中执行。异步

3.dispatch_set_target_queue

dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);这个方法让咱们将一个队列的执行优先级设置为另外一个队列的优先级。第一个参数是变动优先级的队列,第二个参数是目标优先级队列。第一个参数不能是系统提供的Main Queue 和 Global Dispatch Queue。async

//将serialQueue的优先级设置为与concurrentQueue2相同
dispatch_queue_t serialQueue = dispatch_queue_create("com.jiaxiang.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(serialQueue, concurrentQueue2);
复制代码
dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(serialQueue, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"0");
    });
    dispatch_async(serialQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"1");
    });
    dispatch_async(serialQueue2, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"2");
    });
    dispatch_async(serialQueue3, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"3");
    });
    dispatch_async(serialQueue4, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"4");
    });

复制代码

以上建立的五个串行队列在执行的时候是并行执行的,它们每次执行的前后顺序并不固定。可是当咱们使用dispatch_set_target以后它们将变成串行执行。ide

dispatch_queue_t serialQueue1 = dispatch_queue_create("com.jiaxiang.serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("com.jiaxiang.serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("com.jiaxiang.serialQueue3", DISPATCH_QUEUE_SERIAL);
    
    dispatch_set_target_queue(serialQueue1, serialQueue);
    dispatch_set_target_queue(serialQueue2, serialQueue);
    dispatch_set_target_queue(serialQueue3, serialQueue);
    dispatch_set_target_queue(serialQueue4, serialQueue);
    dispatch_async(serialQueue, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"0");
    });
    dispatch_async(serialQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"1");
    });
    dispatch_async(serialQueue2, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"2");
    });
    dispatch_async(serialQueue3, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"3");
    });
    dispatch_async(serialQueue4, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"4");
    });
复制代码

4,dispatch_after

dispatch_after(dispatch_time_t when,dispatch_queue_t queue, dispatch_block_t block);第一个参数指定多长时间之后将任务追加到指定的队列,第二个参数指定执行任务的队列,第三个参数是追加的任务。须要清楚的是任务并非在指定的时间以后执行,而是任务在指定的时间后被追加到队列,所以任务执行的时间极可能在指定的时间以后。而且这个方法执行的是异步任务,所以当指定的队列是main queue的时候也不用担忧死锁。函数

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"Hello");
    });
NSLog(@"Done After");
复制代码

5,Dispatch Group

在使用GCD的时候,咱们有时候会但愿当咱们执行完并行队列以后,可以追加一个任务在全部并行队列中的任务执行完以后再执行。这时候咱们就可使用dispatch group来实现这个需求。post

dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
   
 dispatch_group_async(group, concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"1");
});
    
dispatch_group_async(group, concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"2");
});
    
dispatch_group_async(group, concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"3");
});
    
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"Done");
});
复制代码
+ (void)withGroup:(dispatch_group_t)group 
        sendAsynchronousRequest:(NSURLRequest *)request 
        queue:(NSOperationQueue *)queue 
        completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
{
    if (group == NULL) {
        [self sendAsynchronousRequest:request 
                                queue:queue 
                    completionHandler:handler];
    } else {
        dispatch_group_enter(group);
        [self sendAsynchronousRequest:request 
                                queue:queue 
                    completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
            handler(response, data, error);
            dispatch_group_leave(group);
        }];
    }
}

复制代码

6.dispatch_barrier_async

当咱们须要让并行队列中的1,2,3这几个任务执行完以后,执行任务4,而后再执行任务5,6,7以此避免在在写入数据的同同时读取数据,形成数据竞争,这时候咱们可使用dispatch_barrier_async。

self.serialQueue = dispatch_queue_create("com.serialQueue.com", 0);
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
    key = [key copy];
    dispatch_async(self.serialQueue, ^(){
        if (count == 0) {
            [self.counts removeObjectForKey:key];
        } else {
            self.counts[key] = @(count);
        }
    });
}

- (NSUInteger)countForKey:(NSString *)key;
{
    __block NSUInteger count;
    dispatch_sync(self.serialQueue, ^(){
        NSNumber *n = self.counts[key];
        count = [n unsignedIntegerValue];
    });
    return count;
}
复制代码
self.concurrentQueue = dispatch_queue_creat("com.concurrentQueue.jiaxiang",DISPATCH_QUEUE_CONCURRENT);
- (void)setCount:(NSUInteger)count forKey:(NSString *)key
{
    key = [key copy];
    dispatch_barrier_async(self.isolationQueue, ^(){
        if (count == 0) {
            [self.counts removeObjectForKey:key];
        } else {
            self.counts[key] = @(count);
        }
    });
}

复制代码
dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue1, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"1");
    });
dispatch_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
     NSLog(@"2");
});
    
dispatch_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"3");
});
    
dispatch_barrier_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"4");
});
    
dispatch_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"5");
});
    
dispatch_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"6");
});
    
dispatch_async(concurrentQueue1, ^{
    [NSThread sleepForTimeInterval:1.0];
    NSLog(@"7");
});
复制代码

7.dispatch_apply

该函数按指定的次数将指定的任务追加到队列中,并等待所有处理执行结束。

dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.jiaxiang.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, concurrentQueue1, ^(size_t index) {
    NSLog(@"%zu",index);
});
NSLog(@"Done");
复制代码

8.dispatch_suspend / dispatch_resume

dispatch_suspend(queue),挂起指定的队列,队列中已经执行的任务无影响,为执行的任务中止执行。 dispatch_resume(queue),恢复指定的队列,队列中为执行的任务继续执行。

9.Dispatch Semaphore

  • 经过信号量来控制队列中任务的执行状况。 dispatch_semaphore_t dispatch_semaphore_create(long value):方法接收一个long类型的参数, 返回一个dispatch_semaphore_t类型的信号量,值为传入的参数
  • long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout):接收一个信号和时间值,若信号的信号量为0,则会阻塞当前线程,直到信号量大于0或者通过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行
  • long dispatch_semaphore_signal(dispatch_semaphore_t dsema):使信号量加1并返回
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
   dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

   __block int j = 0;
   dispatch_async(queue, ^{
        j = 100;
        dispatch_semaphore_signal(semaphore);
   });

   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
   NSLog(@"finish j = %zd", j);
复制代码
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

for (int i = 0; i < 100; i++) {
     dispatch_async(queue, ^{
          // 至关于加锁
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          NSLog(@"i = %zd semaphore = %@", i, semaphore);
          // 至关于解锁
          dispatch_semaphore_signal(semaphore);
      });
}

复制代码

10.dispatch_once

dispathc_once函数能够确保某个block在应用程序执行的过程当中只被处理一次,并且它是线程安全的。

+ (Manager *)sharedInstance {
    static Manager *sharedManagerInstance = nil;
    static dispatch_once_t once;

    dispatch_once($once, ^{
        sharedManagerInstance = [[Manager alloc] init];
    });

    return sharedManagerInstance;
}
复制代码
相关文章
相关标签/搜索