返回

底层原理揭秘:OC中的GCD剖析(下篇)

IOS

在上一篇文章中,我们详细探讨了OC中的GCD,重点介绍了其基本概念和工作原理。在这篇文章中,我们将继续深入挖掘GCD,重点分析栅栏函数的应用。

栅栏函数(Barrier)是GCD中一种重要的同步机制,它可以保证在某个队列上执行的任务按照指定的顺序完成。在并发编程中,栅栏函数非常有用,因为它可以防止任务在未完成之前就继续执行,从而避免出现数据竞争和其他并发问题。

栅栏函数有两种类型:并发栅栏函数和全局队列栅栏函数。并发栅栏函数用于保证在同一个队列上执行的任务按照指定的顺序完成,而全局队列栅栏函数用于保证在不同的队列上执行的任务按照指定的顺序完成。

1.并发栅栏函数

并发栅栏函数的使用非常简单,只需在需要同步的任务之前调用dispatch_barrier_async()dispatch_barrier_sync()函数即可。dispatch_barrier_async()函数以异步方式执行栅栏任务,而dispatch_barrier_sync()函数以同步方式执行栅栏任务。

异步并发栅栏函数

以下代码示例演示了如何使用并发栅栏函数来实现异步同步:

dispatch_queue_t queue = dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"Task 1 started");
    sleep(2);
    NSLog(@"Task 1 finished");
});

dispatch_barrier_async(queue, ^{
    NSLog(@"Barrier task started");
    sleep(1);
    NSLog(@"Barrier task finished");
});

dispatch_async(queue, ^{
    NSLog(@"Task 2 started");
    sleep(2);
    NSLog(@"Task 2 finished");
});

运行以上代码,输出结果如下:

Task 1 started
Task 2 started
Barrier task started
Barrier task finished
Task 1 finished
Task 2 finished

从输出结果可以看出,任务1和任务2是并发执行的,而栅栏任务是等到任务1和任务2都执行完之后才开始执行的。这是因为并发栅栏函数以异步方式执行,它不会阻塞队列,而是允许其他任务继续执行。

同步并发栅栏函数

以下代码示例演示了如何使用并发栅栏函数来实现同步同步:

dispatch_queue_t queue = dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"Task 1 started");
    sleep(2);
    NSLog(@"Task 1 finished");
});

dispatch_barrier_sync(queue, ^{
    NSLog(@"Barrier task started");
    sleep(1);
    NSLog(@"Barrier task finished");
});

dispatch_async(queue, ^{
    NSLog(@"Task 2 started");
    sleep(2);
    NSLog(@"Task 2 finished");
});

运行以上代码,输出结果如下:

Task 1 started
Barrier task started
Barrier task finished
Task 1 finished
Task 2 started
Task 2 finished

从输出结果可以看出,任务1、栅栏任务和任务2是顺序执行的。这是因为同步并发栅栏函数以同步方式执行,它会阻塞队列,直到栅栏任务执行完毕之后才允许其他任务继续执行。

2.全局队列栅栏函数

全局队列栅栏函数的使用与并发栅栏函数类似,只需在需要同步的任务之前调用dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);函数即可。

异步全局队列栅栏函数

以下代码示例演示了如何使用全局队列栅栏函数来实现异步同步:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    NSLog(@"Task 1 started");
    sleep(2);
    NSLog(@"Task 1 finished");
});

dispatch_barrier_async(queue, ^{
    NSLog(@"Barrier task started");
    sleep(1);
    NSLog(@"Barrier task finished");
});

dispatch_async(queue, ^{
    NSLog(@"Task 2 started");
    sleep(2);
    NSLog(@"Task 2 finished");
});

运行以上代码,输出结果如下:

Task 1 started
Task 2 started
Barrier task started
Barrier task finished
Task 1 finished
Task 2 finished

从输出结果可以看出,任务1和任务2是并发执行的,而栅栏任务是等到任务1和任务2都执行完之后才开始执行的。这是因为全局队列栅栏函数以异步方式执行,它不会阻塞队列,而是允许其他任务继续执行。

同步全局队列栅栏函数

以下代码示例演示了如何使用全局队列栅栏函数来实现同步同步:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    NSLog(@"Task 1 started");
    sleep(2);
    NSLog(@"Task 1 finished");
});

dispatch_barrier_sync(queue, ^{
    NSLog(@"Barrier task started");
    sleep(1);
    NSLog(@"Barrier task finished");
});

dispatch_async(queue, ^{
    NSLog(@"Task 2 started");
    sleep(2);
    NSLog(@"Task 2 finished");
});

运行以上代码,输出结果如下:

Task 1 started
Barrier task started
Barrier task finished
Task 1 finished
Task 2 started
Task 2 finished

从输出结果可以看出,任务1、栅栏任务和任务2是顺序执行的。这是因为同步全局队列栅栏函数以同步方式执行,它会阻塞队列,直到栅栏任务执行完毕之后才允许其他任务继续执行。

3.总结

通过以上分析,我们可以看到栅栏函数在GCD中起着非常重要的作用。它可以保证在某个队列上执行的任务按照指定的顺序完成,从而避免出现数据竞争和其他并发问题。并发栅栏函数和全局队列栅栏函数各有其特点,我们可以根据不同的需求选择使用不同的栅栏函数。