dispatch queues GCD

咱们几乎能够调度队列去完成全部用线程来完成的任务。调度队列相对于线程代码更简单,易于使用,更高效。编程

    下面讲主要简述调度队列,在应用中如何使用调度队列去执行任务。数据结构


1.关于调度队列并发

   全部的调度队列都是先进先出队列,所以,队列中的任务的开始的顺序和添加到队列中的顺序相同。GCD自动的为咱们提供了一些调度队列,咱们也能够建立新的用于具体的目的。app

   下面列出几种可用的调度队列类型以及如何使用。异步

(1)serial queues(串行队列)又称私有调度队列(private),通常用在对特定资源的同步访问上。咱们能够根据须要建立任意数量的串行队列,每个串行队列之间是并发的。async

(2)并行队列,又称global dispatch queue。并行队列虽然能够并发的执行多个任务,可是任务开始执行的顺序和其加入队列的顺序相同。咱们本身不能去建立并行调度队列。只有三个可用的global concurrent queues。ide

Dispatch Queues异步编程

Dispatch Queues从使用的角度将更象另外一种形式的Operation Queues只是 Operation Queues是用Object C的Dispatch Queues是C的函数

dispatch Queues有serial Queues 也被称为私有dispatch Queues,一个时间只能运行一个task,顺序运行工具

dispatch_queue_t queue;

queue = dispatch_queue_create("myQueue", NULL);  

dispatch_async(queue, ^{

        printf("Do some work here.\n");

    });

    printf("The first block may or may not have run.\n");

    dispatch_sync(queue, ^{

        printf("Do some more work here.\n");

    });

    printf("Both blocks have completed.\n");

这里使用了同步dispatch和异步dispatch,推荐使用dispatch_async这样才能真正体现其中的优点同步至关于WaitUntilDone = YES

 

还有一种就是Concurrent Queues每一个程序系统自动提供了3个Concurrent Queues

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_queue_t aHQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

 dispatch_queue_t aLQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

啥意思一看就明白,3个优先级别的concurrent queues

 

最后一个特殊的Dispatch Queue就是main dispatch Queue 也是程序启动自动生成

dispatch_queue_t mainQueue = dispatch_get_main_queue();

 

concurrent queues和main queue 都是由系统生成并且 dispatch_suspend, dispatch_resume, dispatch_set_context,这些函数对他们无效


(3)main dispatch queue 是一个全局可用的串行队列,其在行用程序的主线程上执行任务。此队列的任务和应用程序的主循环(run loop)要执行的事件源交替执行。由于其运行在应用程序的主线程,main queue常常用来做为应用程序的一个同步点。


2.关于队列的一些技术

除了调度队列,GCD还提供了一些有用的技术来帮助咱们管理代码。

dispath group ,dispatch semaphore, dispath sources


3.使用blocks去实现tasks

block objects是基于C语言的特征,能够用在C,C++ Objective-c中。一个block虽然和函数指针有些类似,可是实际上表明一个底层数据结构,相似与对象,有编译器去建立和管理。

block的一个优点是可使用其本身做用域外的变量,例如,一个block能够读取其父做用域的变量值,此值是copy到了block heap的数据结构中。当block被加入到dispatch queue中,这些值一般为只读形式。

block的声明和函数指针相似,只是把*改成了^,咱们能够传递参数给block,也能够接收其返回的值。


4.建立和管理调度队列

(1)得到全局并发调度队列(global concurrent dispath queues)

系统给每个应用程序提供了三个concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不一样。由于是全局的,咱们不须要去建立。咱们只须要经过使用函数dispath_get_global_queue去获得队列,以下:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

除了获得default的并发队列,还能够经过传递参数DISPATCH_QUEUE_PRIOPITY_HIGH和DISPATCH_QUEUE_PRIOPITY_LOW去获得高优先级或者低优先级的。(第二个参数是为之后扩展保留的)

虽然dispatch queue是引用计数对象,可是在此由于队列是全局的,不须要咱们去retain或者release,咱们须要使用的时候直接调用函数dispath_get_global_queue就能够。


(2)建立串行调度队列

当想要任务按照某一个特定的顺序执行时,串行队列是颇有用的。串行队列在同一个时间只执行一个任务。咱们可使用串行队列代替锁去保护共享的数据。和锁不一样,一个串行队列能够保证任务在一个可预知的顺序下执行。

和并发队列不一样,咱们要本身去建立和管理串行队列,能够建立任意数量的串行队列。当咱们建立串行队列时,应出于某种目的,如保护资源,或者同步应用程序的某些关键行为。

下面的代码表述了怎么建立一个自定义的串行队列,函数dispath_queue_create须要两个参数,队列的名字,队列的属性。调试器和性能工具显示队列的名字帮助咱们去跟踪任务是如何执行,队列的属性被保留供未来使用,应该为NULL

dispatch_queue_t queue; 

queue = dispatch_queue_create("com.example.MyQueue", NULL); 

除了本身建立的自定义队列,系统会自动的给我建立一个串行队列并和应用程序的主线程绑定到一块儿。下面讲述如何得到它。


(3)运行时得到常见的队列

GCD提供了一些函数让咱们可以方便的访问到common dispatch queues

   使用dispatch_get_current_queue函数用来调试或者测试得到当前队列的标识。

   使用函数dispatch_get_main_queue能够获得与应用程序主线程相连的串行调度队列。


(4)调度队列的内存管理

调度队列是引用计数类型,当咱们建立串行调度队列时,咱们要release它。可使用函数dispatch_retain和dispatch_release去增长或者减小引用计数。


(5)在一个队列中存储自定义context information

全部的调度对象容许咱们让其与一个自定义上下文数据关联,经过函数dispatch_set_context和dispatch_get_context来使用,系统不会去使用咱们的自定义数据,咱们本身在恰当的时间去分配和释放。


对于队列,上下文数据一般用来存储一个指向对象的指针,或者其余的数据结构,咱们能够在队列的finalizer函数中去释放context data。下面将给一个例子。


(6)为队列提供一个clean up 函数。

当咱们建立串行调度队列以后,咱们可让其和一个finalizer函数相连用来清理队列中须要清理的数据。咱们可使用 dispatch_set_finalizer_f函数去设置一个函数,当队列的引用计数为0时会去自动的调用。使用此函数去清理和队列相关联的 context data,当context 指针不会NULL时,此函数就会调用。

shows a custom finalizer function and a function that creates a queue and installs that finalizer. The queue uses the finalizer function to release the data stored in the queue’s context pointer. (The myInitializeDataContextFunction and myCleanUpDataContextFunction functions referenced from the code are custom functions that you would provide to initialize and clean up the contents of the data structure itself.) The context pointer passed to the finalizer function contains the data object associated with the queue.

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; 


5.在队列中添加一个任务

(1)

有两种方式在队列中添加一个任务,同步或者异步。尽量使用dispatch_async和dispatch_async_f 函数去执行,比同步的要首选。当咱们向队列中添加一个块对象或者函数时,咱们没有方法去知道此代码什么时间执行。

使用此异步不会去阻塞主线程。

虽然尽量异步添加任务,在有些时候同步的方式去添加一个任务会防止一些同步错误。同步的方式调用函数dispatch_sync和dispatch_sync_f。此函数阻塞主线程的执行,直到指定的任务完成。

下面是代码例子:

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"); 


(2)在任务完成的时候执行completion block

   当任务完成时,咱们应用程序须要获得通知,一遍去合并结果,在传统的异步编程中,咱们可能会使用回调函数,可是在调度队列中,咱们使用completion block。

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); 

   }); 

(3)并发的执行循环迭代(loop iterations)

对于for循环,若是每一次的迭代相互都没有影响,能够并发的去执行迭代,使用函数dispatch_apply或者dispatch_apply_f 函数.

和正常的循环同样,函数dispatch_apply或者dispatch_apply_f直到全部的循环迭代完成时才返回。

以下代码:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

  

dispatch_apply(count, queue, ^(size_t i) { 

   printf("%u\n",i); 

}); 

(4)在主线程上执行任务

咱们能够经过调用函数dispatch_get_main_queue 去去获得主线程的调度队列。

相关文章
相关标签/搜索