iOS - Concurrency Programming Guide (iOS并行编程指南)

iOS开发 专栏收录该内容
9 篇文章 0 订阅


当代码中需要异步执行一些工作的时候,需要用到异步编程的技术。在iOS下进行并行编程的方法有四种:

1. Operation Queues:把要执行的工作打包成一个Objective-C对象,并且该对象的class类型必须继承自NSOperation。

2. Dispatch Queues:把要执行的工作打包成一个函数或一个block对象。有两种该类型的queue:serial dispatch queue,concurrent dispatch queue。区别在于:serial dispatch queue采取FIFO的原则,执行完一个task之后,再去从queue里取出另外一个task执行,也就是说第二个task开始执行时,第一个task肯定都执行完了。而concurrent dispatch queue虽然也是采取FIFO的原则,但后面的task不会等前面的task执行完后再执行,而是真的并行。

3. Dispatch Sources:异步的处理来自系统的事件。

4. 线程:用线程相关的函数创建一个新的线程,在新线程里完成需要异步进行的工作。这是最原始的方法,在iOS上不鼓励。


在平常的工作中,最常用的是 Dispatch Queues ,其他三种几乎不用。下面就重点介绍下 Dispatch Queues 。


Dispatch Queues的类型


类型

描述

Serial

又被称为private dispatch queues,会在一个不同的线程按照FIFO的顺序执行task。可通过dispatch_queue_create创建任意数量的

serial queue,注意的是各个queue之间是完全并行的,但每一个queue内的task是按照顺序一个一个执行的。

Concurrent

又被称为global dispatch queue,task依然会按照FIFO的顺序取出执行,但下一个task不会等待上一个task完成才去执行,task之间

是完全并行的。这些task实际上会被扔到多个不同的线程里并行执行。系统预置了4种global dispatch queue,这4种queue的区别在

于其优先级的不同。另外,因为这些预置的queue是global的,所以不用考虑retain、release的问题。

Main dispatch queue

是一个global的serial queue,只不过与main thread线程绑定了,可以在application的main thread里按照FIFO的顺序执行task。可

以通过dispatch_get_main_queue获取该queue。

相关API

1. dispatch_get_current_queue

这个接口一般用于debug或测试:在block内部调用该接口,可以得到该block被投递到哪个queue里了;在block外部调用该接口,可得到当前默认的concurrent queue。

2. dispatch_get_main_queue

得到当前与main thread绑定的 global Serial dispatch queue。

3. dispatch_get_global_queue

得到当前系统预置的 global Concurrent dispatch queue。

4. dispatch_queue_create

创建一个新的 Serial dispatch queue。


dispatch queue的内存管理

1. 对于自己创建的(private dispatch queue),需要通过 dispatch_retain  dispatch_release 管理queue对象的生存期。

2. 对于 global dispatch queue,系统会自动处理,不用程序员管了。


为queue绑定自定义数据(context data)

可以通过 dispatch_set_context and dispatch_get_context 为所有类型的queue绑定自定义的数据,称之为context,该context数据的 allocate 和 deallocate 由程序员负责。


Serial dispatch queue设置一个clean up的函数

创建一个Serial dispatch queue之后,可以为该queue设置一个clean up函数,该函数会在queue被 deallocate 之前调用,主要用于释放context data。如果没有为该queue绑定context data,也即context指针为NULL,那么该clean up函数将不会被调用。

示例: Installing a queue clean up function

void myFinalizerFunction(void *context)
{
    MyDataContext* theData = (MyDataContext*)context;
 
    // Clean up the contents of the structure
    myCleanUpDataContextFunction(theData);
 
    // Now release the structure itself.
    free(theData);
}
 
dispatch_queue_t createMyQueue()
{
    MyDataContext*  data = (MyDataContext*) malloc(sizeof(MyDataContext));
    myInitializeDataContextFunction(data);
 
    // Create the queue and set the context data.
    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.CriticalTaskQueue", NULL);
    if (serialQueue)
    {
        dispatch_set_context(serialQueue, data);
        dispatch_set_finalizer_f(serialQueue, &myFinalizerFunction);
    }
 
    return serialQueue;
}


向Queue中添加task

有两种方式:同步(synchronously异步(asynchronously

同步API: dispatch_sync 和 dispatch_sync_f

异步API:dispatch_async 和 dispatch_async_f

在实际中,使用同步的方式很少,比如为了避免race condition等。

示例:dispatching tasks asynchronously and synchronously:

dispatch_queue_t myCustomQueue;
myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);
 
dispatch_async(myCustomQueue, ^{
    printf("Do some work here.\n");
});
 
printf("The first block may or may not have run.\n");
 
dispatch_sync(myCustomQueue, ^{
    printf("Do some more work here.\n");
});
printf("Both blocks have completed.\n");


Performing a Completion Block When a Task Is Done

有时候我们希望知道被异步执行的task在什么时候完成,并且能将执行结果返回给我们,这时候就可以在task的末尾再一次通过 dispatch_async 将另一个block dispatch到指定的queue,并将task执行的结果返回给该queue。

示例:Executing a completion callback after a task

void average_async(int *data, size_t len,
   dispatch_queue_t queue, void (^block)(int))
{
   // Retain the queue provided by the user to make
   // sure it does not disappear before the completion
   // block can be called.
   dispatch_retain(queue);
 
   // Do the work on the default concurrent queue and then
   // call the user-provided block with the results.
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      int avg = average(data, len);
      dispatch_async(queue, ^{ block(avg);});
 
      // Release the user-provided queue when done
      dispatch_release(queue);
   });
}


Suspending and Resuming Queues

dispatch_suspend让queue暂停执行blocks,并且将 suspension reference count 加 一

dispatch_resume:恢复执行blocks,并且将 suspension reference count 减 一

只要 suspension reference count 的值大于0,那该queue就是 suspended 状态,因此要平衡好 dispatch_suspend 和 dispatch_resume 的调用次数。


Waiting on Groups of Queued Tasks

如果要阻塞一个线程,以便等待一个或多个task完成,就可以采用 Dispatch groups 技术。相关API为:dispatch_group_async 和 dispatch_group_wait

示例:Waiting on asynchronous tasks

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
 
// Add a task to the group
dispatch_group_async(group, queue, ^{
   // Some asynchronous work
});
 
// Do some other work while the tasks execute.
 
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
 
// Release the group when it is no longer needed.
dispatch_release(group);


  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值