天天看点

iOS OS X 和 iOS 中的多线程技术-4.1 (GCD)

//联系人:石虎 QQ:1224614774 昵称:嗡嘛呢叭咪哄

一、GCD 分发队列

GCD 分发队列是执行任务的有力工具。使用分发队列,你可以异步或者阻塞执行任意多个 block 的代码。你可以使用分发队列来执行几乎任何线程任务。GCD 提供了简单易用的接口。

二、在 GCD 中存在三种队列:

1 串行分发队列(Serial dispatch queue)

串行分发队列又被称为私有分发队列,按顺序执行队列中的任务,且同一时间只执行一个任务。串行分发队列常用于实现同步锁。下面代码创建了一个串行分发队列:

dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue",NULL);

2 并发分发队列(Concurrent dispatch queue)

串行分发队列又被称为全局分发队列,也按顺序执行队列中的任务,但是顺序开始的多个任务会并发同时执行。并发分发队列常用于管理并发任务。下面代码创建了一个并发分发队列:

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

3 主分发队列(Main dispatch queue)

主分发队列是一个全局唯一的特殊的串行分发队列。队列中的任务会被在应用的主线程中执行。主分发队列可以用于执行 UI 相关的操作。取得主分发队列的方法:

dispatch_queue_t mainQueue = dispatch_get_main_queue();

GCD 任务执行方式

GCD 中有两种任务执行方式:

异步执行, dispatch_async,意味将任务放入队列之后,主线程不会等待 block 的返回结果,而是立即继续执行下去。

阻塞执行, dispatch_sync,意味将任务放入队列之后,主线程被阻塞,需要等待 block 的执行结果返回,才能继续执行下去。

GCD 的其他主题

GCD 有着丰富的功能,比如分发组(dispatch group),信号(semaphores),分发栅栏(dispatch barrier),分发源(dispatch source)等等。这些可以用于完成更复杂的多线程任务。详细可以查阅 Apple 关于 GCD 的文档。

使用建议

在能够使用 GCD 的地方,尽量使用 GCD

Apple 公司宣称其在 GCD 技术中为更好地利用多核硬件系统做了很多的优化。所以,在性能方面 GCD 是不用担心的。而且 GCD 也提供了相当丰富的 API,几乎可以完成绝大部分线程相关的编程任务。所以,在多线程相关主题的编程中,GCD 应该是首选。下面举一些可以推荐使用 GCD 的实际例子:

1 使用 GCD 的 dispatch queue 实现同步锁

同步锁的实现方案有不少,比如,如果仅仅是想对某个实例变量的读写操作加锁,可以使用属性(property)的 atomic 参数,对于一段代码加锁可以使用 @synchronized 块,或者 NSLock。

@synchronized 和 NSLock 实现的同步锁:

// Method 1

- (void)synchronizedMethod {

    @synchronized(self) {

        // safe

    }

}

// Method 2

_lock = [[NSLock alloc] init];

- (void)synchronizedMethod {

    [_lock lock];

    // Safe

    [_lock unlock];

}

@synchronized 一般会以 self 为同步对象。重复调用 @synchronized(self) 是很危险的。如果多个属性这么做,每一个属性将会被和其它所有属性同步,这可能并不是你所希望的,更好的方法是每个属性的锁都是相互独立的。

另一种方法是使用 NSLock 实现同步锁,这个方法不错,但是缺点是在极端环境下同步块可能会导致锁死,而且这种情况下处理锁死状态会有麻烦。

一个替代方法是使用 GCD 的分发队列。将读和写分发到相同并发队列中,这样读操作会是并发的,多个线程可以同时执行写操作;而对于写操作,以分发栅栏(dispatch barrier)保证同时只有一个线程可以执行写操作,并且由于写操作无需返回,写操作还是异步马上返回的。这样,就得到了一个高效且线程安全的锁。代码看起来会像这样:

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

- (NSInteger)cake {

    __block NSInteger localCake;

    dispatch_sync(_syncQueue, ^{

        localCake = _cake;

    });

    return localCake;

}

- (void)setCake:(NSInteger)cake {

    dispatch_barrier_async(_syncQueue, ^{

        _cake = cake;

    });

}

简单而言,上面的代码可以使读操作被竞争执行;写操作被互斥执行,并且异步返回。使用 GCD 实现的这个同步锁应该是效率最优且最安全的。

谢谢!!!