既然是多线程,就肯定离不开一个关键的东西——Semaphore(信号量)。
信号量是一个特殊的变量,程序只能在临界区访问它,也就是说只能对它进行原子操作,而且只允许对它进行请求(P操作)和释放(V操作)。信号量的存在是为了保证多线程资源请求的有序性。不理解的自行复习操作系统。
在GCD中,我们用dispatch_semaphore_t来表示信号量,计数为0时等待,计数大于等于1时则无需等待,将信号量减1后直接进行操作。我们使用dispatch_semaphore_create来产生一个Dispatch Semaphore,该函数的参数即为信号量的初始值。

在GCD中有三个函数是semaphore的操作,分别是:

1.创建信号量,可以设置信号量的资源数。0表示没有资源,调用dispatch_semaphore_wait会立即等待。dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
2.等待信号,可以设置超时参数。该函数返回0表示得到通知,非0表示超时。dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
3.通知信号,如果等待线程被唤醒则返回非0,否则返回0。dispatch_semaphore_signal(semaphore);

使用场景一:并发队列

比如现在我每次想执行10个任务。休息两秒。继续执行10个任务。可以这么写.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)testSemaphore{
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);//信号总量是10
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量-1
dispatch_group_async(group, queue, ^{
NSLog(@"%i",i);
sleep(2);
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

使用场景二:异步队列中做事,等待回调后执行某件事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-(void) testAsynFinished{
__block BOOL isok = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
WXDEngine *engine = [[WXDEngine alloc] init];
[engine queryCompletion:^(BOOL isOpen) {//sleep了3秒
isok = isOpen;
dispatch_semaphore_signal(sema);
NSLog(@"success!");
} onError:^(BOOL isOpen, int errorCode) {
isok = NO;
dispatch_semaphore_signal(sema);
NSLog(@"error!");
}];
NSLog(@"finished");
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

使用场景三:生产者,消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-(void) testProductAndConsumer{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t producerQueue = dispatch_queue_create("producer", DISPATCH_QUEUE_CONCURRENT);//生产者线程跑的队列
dispatch_queue_t consumerQueue = dispatch_queue_create("consumer", DISPATCH_QUEUE_CONCURRENT);//消费者线程跑的队列
__block int cakeNumber = 0;
dispatch_async(producerQueue, ^{ //生产者队列
while (1) {
if (!dispatch_semaphore_signal(sem))
{
NSLog(@"Product:生产出了第%d个蛋糕",++cakeNumber);
sleep(1); //wait for a while
continue;
}
}
});
dispatch_async(consumerQueue, ^{//消费者队列
while (1) {
if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, 0*NSEC_PER_SEC))){
if(cakeNumber > 0){
NSLog(@"Consumer:拿到了第%d个蛋糕",cakeNumber--);
}
continue;
}
}
});
}

其实归根结底,中心思想就是通过信号量,来控制线程任务什么时候算作结束,如果不用信号量,请求发出后即认为任务完成,而网络请求又要不同时间,所以会打乱顺序。因此用一个信号量来控制在单个线程操作内,必须等待请求返回,自己要执行的操作完成后,才将信号量+1,这时候一直处于等待的代码也得以执行通过,任务才算作完成。
通过这个方法,就可以解决由于网络请求耗时特性而带来的一些意想不到的多线程处理的问题。

Demo下载