iOS 多线程: dispatch_barrier_async

该篇是 「iOS 多线程」系列的第三篇.

前面两篇可以戳击下面的链接.

iOS 多线程: 正确认识 GCD 队列类型
iOS 多线程: 信号量

该系列博客是为了记录和分享自己关于 iOS 多线程的一些认识, 结合工作中遇到的问题, 和大家交流.

欢迎拍砖.

这里你可以完整的查看 dispatch_barrier 的文档.

关于dispatch_barrier_async, 有这样的描述:

Discussion
Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.

中文翻译大概如下:

一个dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时,barrier block自己开始执行。 之后, 队列继续正常的执行操作。

调用这个函数总是在barrier block被提交之后立即返回,不会等到block被执行。当barrier block到并发队列的最前端,他不会立即执行。相反,队列会等到所有当前正在执行的blocks结束执行。到这时,barrier才开始自己执行。所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。

这里指定的并发队列应该是自己通过dispatch_queue_create函数创建的。如果你传的是一个串行队列或者全局并发队列,这个函数等同于dispatch_async函数。

Get point:

通过dispatch_barrier_async函数提交的任务会等它前面的任务执行完才开始,然后它后面的任务必须等它执行完毕才能开始.
必须使用dispatch_queue_create创建的队列才会达到上面的效果.

看一个例子

 // 自定义并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(concurrentQueue, ^() {
    NSLog(@"dispatch_sync--1");
});

dispatch_sync(concurrentQueue, ^() {
    NSLog(@"dispatch_sync--2");
});

dispatch_barrier_async(concurrentQueue, ^() {
    NSLog(@"dispatch-barrier_async");
    sleep(1);
});

// 同步, (可以改为异步, 3-4顺序就不一致了)
dispatch_sync(concurrentQueue, ^() {
    NSLog(@"dispatch_sync--3");
    sleep(1);
});

dispatch_sync(concurrentQueue, ^() {
    NSLog(@"dispatch_sync--4");
});

不管你执行多少次, 结果都是一样的.

dispatch_sync--1
dispatch_sync--2
dispatch-barrier_async
dispatch_sync--3
dispatch_sync--4

可以看出dispatch-barrier_async阻塞了后面的操作.

上面的执行时在并发队列中,如果在串行队列中呢?

// 自定义串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_SERIAL);

dispatch_async(serialQueue, ^() {
    NSLog(@"dispatch_async--1");
});

dispatch_async(serialQueue, ^() {
    NSLog(@"dispatch_async--2");
});

dispatch_barrier_async(serialQueue, ^() {
    NSLog(@"dispatch-barrier_async");
    sleep(1);
});

// 虽然异步, 但是在串行队列中, 会按照顺序执行
dispatch_async(serialQueue, ^() {
NSLog(@"dispatch_async--3");
    sleep(1);
});

dispatch_async(serialQueue, ^() {
    NSLog(@"dispatch_async--4");
});

执行结果和上面一样.

我们可以利用 dispatch_barrier 的特性实现读写安全的模型.

这里使用缓存来说明.

Cache.h

#import <Foundation/Foundation.h>

@interface Cache : NSObject

+ (instancetype)sharedCache;

- (id)cacheWithKey:(id)key;

- (void)setCacheObject:(id)obj withKey:(id)key;

@end

Cache.m

#import "Cache.h"

static Cache *_instance;

@interface Cache ()

@property (nonatomic, strong) NSMutableDictionary *cache;

@end

@implementation Cache
{
    dispatch_queue_t queue;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });

    return _instance;
}

+ (instancetype)sharedCache
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] _init];
    });

    return _instance;
}

- (instancetype)_init
{
    if (self = [super init]) {
        queue = dispatch_queue_create("cache_gcd_mark", DISPATCH_QUEUE_CONCURRENT);
        _cache = [NSMutableDictionary dictionaryWithCapacity:10];
    }

    return self;
}

- (id)cacheWithKey:(id)key
{
    __block id obj;

    // 任意线程都可以'读'
    dispatch_sync(queue, ^{
        obj = [self.cache objectForKey:key];
    });

    return obj;
}

- (void)setCacheObject:(id)obj withKey:(id)key
{
    // 保证同时'写'的的只有一个
    dispatch_barrier_async(queue, ^{
        [self.cache setObject:obj forKey:key];
    });
}

@end

关于 dispatch_barrier_sync, 大家可以先自行探索.
后面有关于这方面的问题, 再分享给大家.

顺便讨论一下 dispatch_barrier_async_f 函数.

函数原型:

void dispatch_barrier_async_f(dispatch_queue_t queue,
    void *context,
    dispatch_function_t work);

参数:
1. queue : 队列.和 dispatch_barrier_async 一样, 需要自己创建的队列, 才有阻塞的作用.
2. context: void * 类型.可以作为 work 的参数.
3. work : 函数指针.
该函数指针定义如下:

typedef void (*dispatch_function_t)(void *)

举个例子(ARC):

dispatch_queue_t queue = dispatch_queue_create("mark.gcd.barrier", DISPATCH_QUEUE_CONCURRENT);

// 有参数函数        
NSString *msg = @"print info.";
dispatch_function_t func = printInfo;
// 需要使用 bridge 进行转换
dispatch_barrier_async_f(queue, (__bridge void *)(msg), func);

// 无参数       
dispatch_function_t noParamFun = myfunc;
dispatch_async_f(queue, 0, noParamFun);

输出结果如下:

msg: print info.
no param.
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页