本文参考文章链接:
- 巧谈GCD
- iOS多线程详解
- iOS多线程——你要知道的GCD都在这里(这篇存在着一些错误,慎读)
GCD3之dispatch_apply
dispatch_apply
1. 简介
- dispatch_apply类似一个for循环,会在指定的dispatch queue中运行block任务n次。如果队列是并发队列,会开启多线程,并发执行block任务,;如果是串行队列,则会顺序执行。也就是说,dispatch_apply在并发队列下,有开启多线程的能力。
- dispatch_apply函数在返回之前等待任务完成,block任务执行n次后才返回,这点和同步执行相同。
- 使用dispatch_apply并发队列,效率要高于for循环。
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
参数说明:
第一个参数:迭代次数
第二个参数:队列
block的参数:(size_t 无符号整型),随便写一个参数,应该是为了并发时区分各个block用的
2. 例子,并发队列:
全局并发队列。index是随便写的
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%ld %@",index++,[NSThread currentThread]);
});
NSLog(@"done");
输出结果:
2018-08-18 12:15:05.663343+0800 GCD[1117:113861] 0 <NSThread: 0x600000261c40>{number = 1, name = main}
2018-08-18 12:15:05.663348+0800 GCD[1117:113947] 2 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:15:05.663353+0800 GCD[1117:113945] 3 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663394+0800 GCD[1117:113946] 1 <NSThread: 0x60000047e3c0>{number = 3, name = (null)}
2018-08-18 12:15:05.663561+0800 GCD[1117:113945] 5 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663561+0800 GCD[1117:113947] 4 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:15:05.663563+0800 GCD[1117:113861] 6 <NSThread: 0x600000261c40>{number = 1, name = main}
2018-08-18 12:15:05.663563+0800 GCD[1117:113946] 7 <NSThread: 0x60000047e3c0>{number = 3, name = (null)}
2018-08-18 12:15:05.663672+0800 GCD[1117:113945] 8 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663809+0800 GCD[1117:113947] 9 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:21:07.765380+0800 GCD[1134:118115] done
- index是从0开始的,因为是无符号整型。
- 输出顺序不一样。因为是并发队列,交给了多个线程去执行。
- 最后才输出done。因为dispatch_apply必须等待任务完成才能返回。
3. 例子,串行队列(并没有什么用,所以还是使用并行队列去吧)
dispatch_queue_t serialQueue = dispatch_queue_create("xds, DISPATCH_QUEUE_SERIAL);
dispatch_apply(10, serialQueue, ^(size_t index) {
NSLog(@"%ld %@",index++,[NSThread currentThread]);
});
输出结果:
2018-08-18 12:27:06.990331+0800 GCD[1166:122439] 0 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990560+0800 GCD[1166:122439] 1 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990678+0800 GCD[1166:122439] 2 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990793+0800 GCD[1166:122439] 3 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990936+0800 GCD[1166:122439] 4 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991104+0800 GCD[1166:122439] 5 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991220+0800 GCD[1166:122439] 6 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991335+0800 GCD[1166:122439] 7 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991456+0800 GCD[1166:122439] 8 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991566+0800 GCD[1166:122439] 9 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991735+0800 GCD[1166:122439] done
- 顺序执行,因为是串行队列,只能执行完上一个任务,才能执行下一个任务。
- 最后输出done。因为dispatch_apply等待任务完成才能返回。
- 只有一个主线程。
使用dispatch_apply串行队列并没有什么用,所以还是使用并行队列去吧。
4. 应用方法
- 直接使用dispatch_apply并发队列。这样会使用主线程处理一部分block。
- 完全使用子线程并发处理任务:在异步执行dispatch_asyn中使用dispatch_apply并发队列,注意并发队列不能是同一个,否则只会使用同一个线程顺序处理。
在某些场景下使用dispatch_apply会对性能有很大的提升,比如你的代码需要以每个像素为基准来处理计算image图片。同时dispatch apply能够避免一些线程爆炸的情况发生(创建很多线程)。
5. 应用场景
应用场景1
可以用来拉取网络数据后提前算出各个控件的大小,防止绘制时计算,提高表单滑动流畅性,如果用for循环,耗时较多,并且每个表单的数据没有依赖关系,所以用dispatch_apply比较好****
应用场景2
如果我们从服务器获取一个数组的数据,那么我们可以使用该方法从而****快速的批量字典转模型,然后转到主线程去更新UI****。
有一个字典数组,快速的字典转模型。
- 不使用主线程,完全使用子线程去处理。
注意:这样要使用两个不同的并发队列,才能不使用主线程并且并发执行。
代码示例如下:
NSArray *dictArray = nil;//存放从服务器返回的字典数组
dispatch_queue_t queue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue1, ^{
dispatch_apply(dictArray.count, queue, ^(size_t index){
//字典转模型
NSLog(@"%ld %@", index++,[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主线程更新");
});
});
将dictArray.count用10代替,输出结果如下。发现是多线程并发,且没有主线程参与。
2018-08-18 12:47:54.536758+0800 GCD[1226:136959] 3 <NSThread: 0x600000269380>{number = 6, name = (null)}
2018-08-18 12:47:54.536772+0800 GCD[1226:136956] 2 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.536779+0800 GCD[1226:136958] 1 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.536797+0800 GCD[1226:136960] 0 <NSThread: 0x600000268ec0>{number = 3, name = (null)}
2018-08-18 12:47:54.537036+0800 GCD[1226:136958] 5 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.537049+0800 GCD[1226:136956] 6 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.537060+0800 GCD[1226:136959] 4 <NSThread: 0x600000269380>{number = 6, name = (null)}
2018-08-18 12:47:54.537127+0800 GCD[1226:136960] 7 <NSThread: 0x600000268ec0>{number = 3, name = (null)}
2018-08-18 12:47:54.537287+0800 GCD[1226:136958] 8 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.538363+0800 GCD[1226:136956] 9 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.545784+0800 GCD[1226:136876] 主线程更新
- 可以使用主线程处理一部分任务。直接使用dispatch_apply.
NSArray *dictArray = nil;//存放从服务器返回的字典数组
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(dictArray.count, queue, ^(size_t index){
//字典转数组
NSLog(@"%ld %@", index++,[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主线程更新");
});
将dictArray.count换成10,输出结果如下。发现是多线程并发,且会有主线程在其中。
2018-08-18 12:54:50.402604+0800 GCD[1246:142090] 2 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.402604+0800 GCD[1246:142089] 3 <NSThread: 0x604000469040>{number = 4, name = (null)}
2018-08-18 12:54:50.402608+0800 GCD[1246:141938] 0 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.402655+0800 GCD[1246:142088] 1 <NSThread: 0x600000079380>{number = 3, name = (null)}
2018-08-18 12:54:50.402925+0800 GCD[1246:142090] 7 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.402926+0800 GCD[1246:141938] 6 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.402926+0800 GCD[1246:142088] 5 <NSThread: 0x600000079380>{number = 3, name = (null)}
2018-08-18 12:54:50.402945+0800 GCD[1246:142089] 4 <NSThread: 0x604000469040>{number = 4, name = (null)}
2018-08-18 12:54:50.403045+0800 GCD[1246:142090] 8 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.403064+0800 GCD[1246:141938] 9 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.407587+0800 GCD[1246:141938] 主线程更新
GCD4之dispatch_after
dispatch_after
1. 简介
dispatch_after(dispatch_time_t when,dispatch_queue_t queue, dispatch_block_t block);
官方文档
This function waits until the specified time and then asynchronously adds block to the specified queue.
Passing DISPATCH_TIME_NOW as the when parameter is supported, but is not as optimal as calling dispatch_async instead. Passing DISPATCH_TIME_FOREVER is undefined.
翻译:
此函数等待直到指定的时间,然后异步地将块添加到指定的队列。
传递DISPATCH_TIME_NOW作为支持的when参数,但不如调用dispatch_async那样最优。 传递DISPATCH_TIME_FOREVER未定义。
- dispatch_after,是在指定时间安排一个任务提交给指定的队列,而不是在指定时间去执行任务;
- dispatch_after是一个异步执行,也就是说提交任务后马上返回,如果是串行队列则一个个执行;如果是并发队列则并发执行;
- 因为是异步,有可能开启多线程。
2. 一般用法
dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
// 在queue里面延迟执行的一段代码
...
});
3. 例子
在主队列延迟提交一个任务。有可能开启其它线程。
NSLog(@"1111 %@", [NSThread currentThread]);
dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"同步 %@", [NSThread currentThread]);
});
NSLog(@"2222 %@", [NSThread currentThread]);
2018-08-18 13:21:13.941606+0800 GCD[1278:159101] 1111 <NSThread: 0x604000073400>{number = 1, name = main}
2018-08-18 13:21:13.941915+0800 GCD[1278:159101] 2222 <NSThread: 0x604000073400>{number = 1, name = main}
2018-08-18 13:21:19.415315+0800 GCD[1278:159101] 同步 <NSThread: 0x604000073400>{number = 1, name = main}
4. 应用场景
这为我们提供了一个简单的延迟执行的方式,比如在view加载结束延迟执行一个动画等等。
GCD5之dispatch_barrier_(a)sync
1. dispatch_barrier_async
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
官方文档说明:
Submits a barrier block for asynchronous execution and returns immediately.
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_queue_create函数自己创建的并发队列。 如果传递给此函数的队列是串行队列或全局并发队列之一,则此函数的行为类似于dispatch_async函数。
理解:
- 异步执行,立即返回
- 相当于一个承前启后的作用,它前面的执行完成后,执行它,再执行后面的任务
- 必须使用并发队列,且得是dispatch_queue_create自己创建的并发队列。串行队列和全局队列没有用
- 实验后得知,必须是同一个并发队列的任务才会起到承前启后的作用。
例子
// dispatch_barrier_async的作用可以用一个词概括--承上启下,它保证此前的任务都先于自己执行,此后的任务也迟于自己执行。本例中,任务4会在任务1、2、3都执行完之后执行,而任务5、6会等待任务4执行完后执行。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 任务1
...
});
dispatch_async(queue, ^{
// 任务2
...
});
dispatch_async(queue, ^{
// 任务3
...
});
dispatch_barrier_async(queue, ^{
// 任务4
...
});
dispatch_async(queue, ^{
// 任务5
...
});
dispatch_async(queue, ^{
// 任务6
...
});
2. 应用场景
和dispatch_group类似,dispatch_barrier也是异步任务间的一种同步方式,可以在比如文件的读写操作时使用,保证读操作的准确性。
3. dispatch_barrier_sync
和dispatch_barrier_async作用相同,只是有可能造成死锁,要注意。
官方文档
Submits a barrier block to a dispatch queue for synchronous execution. Unlike dispatch_barrier_async, this function does not return until the barrier block has finished. Calling this function and targeting the current queue results in deadlock.
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 queue executes the barrier block 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_sync function.
Unlike with dispatch_barrier_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As an optimization, this function invokes the barrier block on the current thread when possible.
翻译如下
将屏障块提交到调度队列以进行同步执行。与dispatch_barrier_async不同,此功能在屏障块完成之前不会返回。调用此函数并以当前队列为目标会导致死锁。
当障碍块到达专用并发队列的前面时,它不会立即执行。相反,队列等待,直到其当前正在执行的块完成执行。此时,队列自己执行屏障块。在屏障块完成之后,在屏障块之后提交的任何块都不会执行。
您指定的队列应该是您使用dispatch_queue_create函数自己创建的并发队列。如果传递给此函数的队列是串行队列或全局并发队列之一,则此函数的行为类似于dispatch_sync函数。
与dispatch_barrier_async不同,不对目标队列执行保留。因为对此函数的调用是同步的,所以它“借用”调用者的引用。此外,不对块执行Block_copy。
作为优化,此函数在可能的情况下调用当前线程上的屏障块。
关于单例模式的两篇文章链接:
GCD6之dispatch_once及单例模式
dispatch_once
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
- 参数predicate介绍
predicate:
A pointer to a dispatch_once_t structure that is used to test whether the block has completed or not.
谓词:
指向dispatch_once_t结构的指针,用于测试块是否已完成。
- 简介
Executes a block object once and only once for the lifetime of an application.
在应用程序的生命周期内执行一次且仅执行一次块对象。
该方法能够保证在应用的生命周期内只执行一次提交的任务,所以常用于单例类的创建。
- 一般创建:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行一次的任务
...
});
- 一个单例类的创建:
@interface MyUtil: NSObject <NSCopying,NSMutbaleCopying>
+ (instancetype)sharedInstance;
@end
@implementation MyUtil
static MyUtil *staticMyUtil = nil;
+ (instancetype)sharedInstance
{
//保证初始化创建只执行一次,注意是super
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
staticMyUtil = [[super allocWithZone:nil] init];
});
return staticMyUtil;
}
//防止通过alloc或new直接创建对象
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
return [self sharedInstance]
}
//实现NSCopying协议的方法,防止通过copy获取副本对象
- (instancetype)copyWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
//实现NSCopying协议的方法,防止通过copy获取副本对象
- (instancetype)mutableCopyWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
@end
GCD7之dispatch_group
1. dispatch_group_t
typedef NSObject<OS_dispatch_group> *dispatch_group_t;
调度组,用于提交一组任务到组里。
A dispatch group is a mechanism for monitoring a set of blocks. Your application can monitor the blocks in the group synchronously or asynchronously depending on your needs. By extension, a group can be useful for synchronizing for code that depends on the completion of other tasks.
Note that the blocks in a group may be run on different queues, and each individual block can add more blocks to the group.
The dispatch group keeps track of how many blocks are outstanding, and GCD retains the group until all its associated blocks complete execution.
调度组是用于监视一组块的机制。 您的应用程序可以根据您的需要同步或异步监视组中的块。 通过扩展,一个组可用于同步依赖于其他任务完成的代码。
请注意,组中的块可以在不同的队列上运行,并且每个单独的块可以向该组添加更多块。
调度组会跟踪未完成的块数,GCD会保留该组,直到其所有关联的块完成执行。
2. dispatch_group_create
用于创建dispatch_group_t调度组
dispatch_group_t dispatch_group_create(void);
3. dispatch_group_async
void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
Submits a block to a dispatch queue and associates the block with the specified dispatch group.The dispatch group can be used to wait for the completion of the block objects it references.
将任务异步提交到调度队列,并将任务与指定的调度组关联。调度组可用于等待它引用的块对象的完成。
等待group的任务都执行完毕,可以调用下面两个方法。
4. dispatch_group_notify
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
Schedules a block object to be submitted to a queue when a group of previously submitted block objects have completed.
在一组先前提交的任务完成时,将任务提交到指定队列。
5. dispatch_group_wait
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
和dispatch_group_notify功能类似(多了一个dispatch_time_t参数可以设置超时时间),在group上任务完成前,dispatch_group_wait会阻塞当前线程(所以不能放在主线程调用)一直等待;当group上任务完成,或者等待时间超过设置的超时时间会结束等待;
简单说来,多了一个定时器,会阻塞当前线程,直到组内任务完成,或者定时器到了,然后将任务提交给指定队列。
6. 补充
dispatch_group会等和它关联的所有的dispatch_queue_t上的任务都执行完毕才会发出同步信号(dispathc_group_notify的代码块block会被执行,group_wati会结束等待)。也就是说一个group可以关联多个任务队列。
这点和dispatch_barrier不一样,dispatch_barrier只对同一个队列上的有用。
7. 代码示例
- (void)groupSync
{
dispatch_queue_t disqueue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t disgroup = dispatch_group_create();
dispatch_group_async(disgroup, disqueue, ^{
NSLog(@"任务一完成");
});
dispatch_group_async(disgroup, disqueue, ^{
sleep(8);
NSLog(@"任务二完成");
});
dispatch_group_notify(disgroup, disqueue, ^{
NSLog(@"dispatch_group_notify 执行");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_wait(disgroup, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
NSLog(@"dispatch_group_wait 结束");
});
}
输出:
2018-08-18 16:12:24.053425+0800 GCD[1568:268349] 任务一完成
2018-08-18 16:12:29.054188+0800 GCD[1568:268347] dispatch_group_wait 结束
2018-08-18 16:12:32.057110+0800 GCD[1568:268348] 任务二完成
2018-08-18 16:12:32.057352+0800 GCD[1568:268348] dispatch_group_notify 执行
分析:任务1输出,任务二睡8秒,wait睡5秒,wait输出,任务2醒来后输出,notify输出
关于dispatch_group_enter和dispatch_group_leave的介绍及使用,看这个链接
应用场景
信号量这篇总结的不太好,可以去找一下别的文章再看一下
GCD8之dispatch_semaphore(信号量)
dispatch_semaphore_create
dispatch_semaphore_t dispatch_semaphore_create(long value);
传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量,long必须大于等于0。
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
这个函数会使传入的信号量dsema的值加1
dispatch_semaphore_wait
注意,正常的使用顺序是先降低然后再提高,dispatch_semaphore_signal和dispatch_semaphore_wait这两个函数通常成对使用。
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;
如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float型数),其所处线程自动执行其后语句
dispatch_time_t
在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER。
DISPATCH_TIME_NOW 表示当前;
DISPATCH_TIME_FOREVER 表示遥远的未来;
一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。
创建dispatch_time_t类型的变量有两种方法,dispatch_time和dispatch_walltime。
利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量。
dispatch_time的声明如下:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示when加delta时间就是timeout的时间。
例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);
表示当前时间向后延时一秒为timeout的时间。
(6)关于信号量,一般可以用停车来比喻。
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal
就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;
当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主
没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,
所以就一直等下去。
GCD9之dispatch_set_context、dispatch_get_context和dispatch_set_finalizer_f
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。
需使用下面abc三种方式创建context,并且一般结合dispatch_set_finalizer_f使用,回收context内存。
// dispatch_set_context、dispatch_get_context是为了向队列中传递上下文context服务的。
// dispatch_set_finalizer_f相当于dispatch_object_t的析构函数。
// 因为context的数据不是foundation对象,所以arc不会自动回收,一般在dispatch_set_finalizer_f中手动回收,所以一般讲上述三个方法绑定使用。
- (void)test
{
// 几种创建context的方式
// a、用C语言的malloc创建context数据。
// b、用C++的new创建类对象。
// c、用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
if (queue) {
// "123"即为传入的context
dispatch_set_context(queue, "123");
dispatch_set_finalizer_f(queue, &xigou);
}
dispatch_async(queue, ^{
char *string = dispatch_get_context(queue);
NSLog(@"%s", string);
});
}
// 该函数会在dispatch_object_t销毁时调用。
void xigou(void *context)
{
// 释放context的内存(对应上述abc)
// a、CFRelease(context);
// b、free(context);
// c、delete context;
}