有一个面试题,觉得不错,开启三个异步线程task1, task2, task3, task1和task2并发执行,task3在task1和task2执行之后执行,如何实现?
这个题比较容易,实现的方式很多种,例如dispatch_group_t, dispatch_barrier_async都可以,不赘述。
Talk is cheap, show me the code.
代码如下:
pthread_t thread1, thread2, thread3;
pthread_mutex_t cont_var_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cont_var = PTHREAD_COND_INITIALIZER;
int global = 0;
void* functionForThread1()
{
NSLog(@"do task1 here");
if (global < 2) {
pthread_mutex_lock(&cont_var_lock);
global++;
if (global == 2) {
pthread_cond_signal(&cont_var);
}
pthread_mutex_unlock(&cont_var_lock);
}
pthread_exit(NULL);
}
void* functionForThread2()
{
NSLog(@"do task2 here");
if (global < 2) {
pthread_mutex_lock(&cont_var_lock);
global++;
if (global == 2) {
pthread_cond_signal(&cont_var);
}
pthread_mutex_unlock(&cont_var_lock);
}
pthread_exit(NULL);
}
void* functionForThread3()
{
if (global != 2) {
pthread_cond_wait(&cont_var, &cont_var_lock);
}
NSLog(@"do task3 here");
pthread_exit(NULL);
}
执行:
pthread_create(&thread1, NULL, &functionForThread1, NULL);
pthread_create(&thread2, NULL, &functionForThread2, NULL);
pthread_create(&thread3, NULL, &functionForThread3, NULL);
当然,不用锁的话,pthread_join更简单。
进阶拷问:如果只能用dispatch_semaphore_t, 如何实现?
这个就有意思了
dispatch_queue_t queue = dispatch_queue_create("com.zhangbo.queue", DISPATCH_QUEUE_CONCURRENT);
__block int global = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
int ret = -99; // non-zero value
do {
ret = pthread_mutex_lock(&cont_var_lock);
global++;
pthread_mutex_unlock(&cont_var_lock);
} while (ret != 0);
NSLog(@"do task1 here");
if (global == 2) {
dispatch_semaphore_signal(semaphore);
}
});
dispatch_async(queue, ^{
int ret = -99;
do {
ret = pthread_mutex_lock(&cont_var_lock);
global++;
pthread_mutex_unlock(&cont_var_lock);
} while (ret != 0);
NSLog(@"do task2 here");
if (global == 2) {
dispatch_semaphore_signal(semaphore);
}
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"do task3 here");
task1与task2也可以在异步线程的开始就可以执行,因为时间不同,如果一开始就取global,那么有可能都取出0,所以要loop
另一个方法,用两个信号量
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t sema_first = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
NSLog(@"task1 done");
dispatch_semaphore_signal(sema_first);
});
dispatch_semaphore_wait(sema_first, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
!self.smallBlk ? : self.smallBlk();
});
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t sema_second = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
NSLog(@"task2 done");
dispatch_semaphore_signal(sema_second);
});
dispatch_semaphore_wait(sema_second, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
!self.smallBlk ? : self.smallBlk();
});
});
可以block住task1和task2的线程,然后执行完毕回到其他线程,这里是主线程,由于dispatch_get_main_queue() 是一个串行队列,So,
self.smallBlk = ^{
global++;
if (global == 2) {
NSLog(@"do task3 here");
}
};
多线程是一个很锻炼思维的东西,过于依赖GCD,或者NSOperation的话,慢慢的多线程概念就模糊了。再说个思路,看过libDispatch.dylib源码的小伙伴们,可以知道dispatch_group_t是用信号量实现的。enter和leave到notify并没有阻塞线程。这个思路我觉得也可以实现一把。