iOS-线程依赖的处理方法总结
一 序言
在iOS开发中,我们经常会用到一个线程需要等待另一个结束才能进行的需求,这种需求其实有很实用的解决办法.下面我将列举一些目前用到的两种方式.
一 线程同步
-
线程同步,字面意思好像是多个线程一起工作.
-
其实不然,这里的同是协同,互相配合的意思,也就是多个线程互相配合,按照预定顺序依次执行的意思.
-
我们平时所用的各种锁(比如NSLock,OSSpinLock,NSRecursiveLock)也是线程同步的一部分
-
系统提供的
atomic
保证了属性的原子性
,但是仅仅只是在getter
和setter
的时候使用自旋锁
,并不是线程安全.比如多个线程对可变数组的增删
二 信号量简介
- 关于信号量简介 信号量简介
特性
抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态
。在信号量上我们定义两种操作: Wait(等待)
和 Release(释放)
。当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
如果信号量大于等于一时,做减一操作,然后往下执行。否则一直等下去(指放入阻塞队列),直到信号量大于等于一。
dispatch_semaphore_signal(semaphore)
释放资源,信号量加一操作
三 GCD
这里使用 GCD 来实现线程依赖
- 1.GCD 没有添加线程依赖
- (void)gcdDependTest2 {
// 线程一做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程一做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch1 semaphore send");
});
// 线程二做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程二做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch2 semaphore send");
});
// 线程二做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程三做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch3 semaphore send");
});
NSLog(@"function end");
}
运行结果
GCD无依赖.png- 2.GCD添加线程使用信号量添加线程依赖实例代码
/// 让线程一 线程二 线程三顺序执行
- (void)gcdDependTest3 {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); // 信号量初始化为0
// 线程一做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程一做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch1 semaphore send");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 线程二做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程二做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch2 semaphore send");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 线程二做事情
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"线程三做事情");
for (int i = 0; i < 10000; i++) {
//just for delayed
}
sleep(1);
NSLog(@"dispatch3 semaphore send");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"function end");
}
运行结果
GCD添加依赖.png1.通过执行结果可知,通过信号量的确可以给
GCD
线程添加依赖。
2.dispatch_semaphore_signal(semaphore)
方法必须再另一个线程中调用
3.dispatch_semaphore_wait()
一定不能在主线程中调用,因为一不小心就会阻塞当前线程,造成主线程卡死。
四 GCD - dispatch_barrier_async
1.dispatch_barrier_async():dispatch_barrier_sync():使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。
2.说明dispatch_barrier_async的顺序执行还是依赖queue的类型,必需要queue的类型为dispatch_queue_create
创建的,而且attr
参数值必需是DISPATCH_QUEUE_CONCURRENT
类型,前面两个非dispatch_barrier_async
的类型的执行是依赖其本身的执行时间的,如果attr
如果是DISPATCH_QUEUE_SERIAL时,那就完全是符合Serial queue的FIFO特征了。
- 1.实例代码如下
DISPATCH_QUEUE_CONCURRENT
类型
- (void)dispatchBarrierAsyncTest {
dispatch_queue_t queue = dispatch_queue_create("gcd.barrier.async", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:0.5];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
}
运行结果如下
DISPATCH_QUEUE_CONCURRENT.png- 2.实例代码如下
DISPATCH_QUEUE_SERIAL
类型
- (void)dispatchBarrierAsyncTest {
dispatch_queue_t queue = dispatch_queue_create("gcd.barrier.async", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:0.5];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
}
执行结果
DISPATCH_QUEUE_SERIAL.png- 3.如果队列采用
dispatch_get_global_queue
创建,并且参数为DISPATCH_QUEUE_PRIORITY_DEFAULT
- (void)dispatchBarrierAsyncTest {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:0.5];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});
}
执行结果如下
image.png五 NSOperationQueue
- 实例代码
- (void)nsoperationDependTest {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 1000; i++) {
}
sleep(1);
NSLog(@"op1 is finish");
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 10000; i++) {
}
sleep(1);
NSLog(@"op2 is finish");
}];
[op1 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
}
执行结果
NSOperationQueue.png本文参考
ios 几种线程依赖的处理方式
GCD之dispatch queue深入浅出