wait
wait之后的操作是在wait之前的线程任务操作完之后才进行的,哪怕wait之前的任务是异步的且放在并发队列里的(即异步执行)。
代码:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 1000; i ++) {
NSLog(@"%lu", i);
}
NSLog(@"waiting...");
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"end");
如果没有dispatch_group_wait,是如何打印的呢?end肯定是在waiting之前就打印了。
看打印:
GCD-wait[1071:72264] 997
GCD-wait[1071:72264] 998
GCD-wait[1071:72264] 999
GCD-wait[1071:72264] waitting...
GCD-wait[1071:72065] end
打印end可以在异步执行的任务打印waiting之后进行。 使用wait会阻塞当前线程。
barrier(栅栏)
barrier的作用和wait类似,它就像一个栅栏一样拦住了同一个队列中其前面的操作,让其之后的所有操作都在其之前的所有操作完成之后才能进行。但它不会像wait一样将其他线程中的操作也拦住。
代码:
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
// 为什么使用全局队列不行?
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 1000; i ++) {
NSLog(@"%lu", i);
}
NSLog(@"waiting...");
});
dispatch_barrier_async(queue, ^{
NSLog(@"barrier");
});
dispatch_async(queue, ^{
NSLog(@"barrier之后的任务...");
});
NSLog(@"end");
如果没有dispatch_barrier_async是如何打印的呢?“waiting”和“barrier之后的任务...”肯定是交替打印的,而且"end"会早早地就被打印了。
看打印:
GCD-barrier[1412:136078] end
GCD-barrier[1412:137056] 0
GCD-barrier[1412:137056] 1
GCD-barrier[1412:137056] 998
GCD-barrier[1412:137056] 999
GCD-barrier[1412:137056] waitting...
GCD-barrier[1412:137056] barrier
GCD-barrier[1412:137056] barrier之后的任务...
注意:这里的异步队列不能是全局异步队列(官方规定)
dispatch_group_notify
我们同样可以利用notify监听队列中的任务,当所有任务都执行完之后,再进行notify中的操作。
semaphore
可以将异步任务转成同步任务。
代码:
__block NSInteger num = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 1000; i ++) {
NSLog(@"%lu", i);
num ++;
}
NSLog(@"waiting...");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"num=%lu", num);
NSLog(@"end");
如果没有semaphoreend肯定会提前打印,而且打印的num会是0。
(dispatch_semaphore_signal对信号进行+1,dispatch_semaphore_wait对信号进行-1,如果信号为0,阻塞线程)
看打印:
GCD-Semaphore[1947:219353] 997
GCD-Semaphore[1947:219353] 998
GCD-Semaphore[1947:219353] 999
GCD-Semaphore[1947:219353] waiting...
GCD-Semaphore[1947:218743] num=1000
GCD-Semaphore[1947:218743] end
开发中如果有需要block回调之后才能进行的操作我们可以使用semaphore将代码放在block之外。
加锁
代码:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
for (NSInteger i = 0; i < 100; i ++) {
[self.mutableArr addObject:@(i)];
}
semaphoreLock = dispatch_semaphore_create(1);
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue1, ^{
[self nslog];
});
dispatch_async(queue2, ^{
[self nslog];
});
dispatch_async(queue3, ^{
[self nslog];
});
}
- (void)nslog{
while (1) {
// 加锁
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.mutableArr.count > 0) {
[self.mutableArr removeObjectAtIndex:0];
NSLog(@"%lu--%@", self.mutableArr.count, [NSThread currentThread]);
[NSThread sleepForTimeInterval:0.2];
}else{
dispatch_semaphore_signal(semaphoreLock);
break;
}
// 相当于解锁
dispatch_semaphore_signal(semaphoreLock);
}
}
如果不加锁打印内容:
GCD-线程安全[2271:260744] 3--<NSThread: 0x60000013e580>{number = 6, name = (null)}
GCD-线程安全[2271:266057] 2--<NSThread: 0x60000013d900>{number = 4, name = (null)}
GCD-线程安全[2271:266058] 2--<NSThread: 0x600000136740>{number = 5, name = (null)}
GCD-线程安全[2271:260744] 0--<NSThread: 0x60000013e580>{number = 6, name = (null)}
GCD-线程安全[2271:266057] 1--<NSThread: 0x60000013d900>{number = 4, name = (null)}
我们可以看到打印的数据混乱,如果不加锁,某一刻两个线程读到数组中的数据只剩下一个元素都进入到if判断中,其中一个线程将数组中最后一个元素移除,此时数组为空,另一个线程再对空数组做移除操作,此时就会发生崩溃,这样看来线程是不安全的。
加锁后的打印:
GCD-线程安全[2428:278050] 4--<NSThread: 0x600003edd180>{number = 5, name = (null)}
GCD-线程安全[2428:278048] 3--<NSThread: 0x600003e87140>{number = 3, name = (null)}
GCD-线程安全[2428:278051] 2--<NSThread: 0x600003e85800>{number = 4, name = (null)}
GCD-线程安全[2428:278050] 1--<NSThread: 0x600003edd180>{number = 5, name = (null)}
GCD-线程安全[2428:278048] 0--<NSThread: 0x600003e87140>{number = 3, name = (null)}
加锁后的打印数据是正常的,并且不会由于对空数组进行操作造成程序崩溃。
本篇文章到这里就结束了,愿大家加班不多工资多,男同胞都有女朋友,女同胞都有男朋友。😊