关于GCD(详细)

来源:互联网 发布:奥地利经济学派 知乎 编辑:程序博客网 时间:2024/05/22 03:29
使用GCD需要注意的地方:
1)更新UI只能在主线程中执行。
2)使用线程不安全的对象要注意
3)创建的dispatch_queue_t要release掉,否则内存会报错。
//==============================================
GCD之dispatch queues:
1、一个dispatch queue是一个像类型的结构,管理着你提交给它的tasks。所有的dispatch queue都是FIFO(先进先出)的数据结构。因此,你添加到queue的task总是以它们添加的顺序启动。GCD自动为你提供了一些dispatch queues,但是你可以创建自己的queue。下面列出了你可以使用的dispatch queue的类型:
1)Searial类型:也称为private dispatch queue,按照添加的顺序在一个时间执行一个task。当前执行的task运行于一个单独的线程(不同的task可能有不同的线程)。Serial queue经常用作同步访问到一个特定的资源。 你可以创建任意多的serial queue,并且每个queue可以和其他queue同时操作。换句话说,如果逆创建了4个serial queue,每个queue在同一时刻只能执行一个task,但是4个queue可以同时执行4个task。
2)Concurrent类型:也称为global dispatch queue,可以同时执行多个task,但是task仍然是按照添加顺序开始执行。同时执行的task在各自的线程中执行。同时执行的task数量在任意时刻都是不同的,这取决于系统条件。你不能自己创建concurrent类型的queue。取而代之的,有3个globla concurrent queue供你使用。
使用dispatch_get_global_queue函数获得。有3个不同的优先级.
3)Main dispatch queue:这个queue是全局的serial queue,代表主线程。这个queue工作于应用的run loop。

使用dispatch queue需要注意的:
1)系统决定了同时执行task的总数量。例如逆创建了100个task在100个queue里,它们可能不能同时执行(除非系统又100个有效的cores)。
2)queue的priority影响系统计算启动哪个task。
3)被加入到queue的task必须随时准备好运行。
4)private dispatch queue是reference-counted 对象。符合内存管理规则。

2、QUeue-Related技术
1)Dispatch Groups:用来监视一组block对象的完成。你可以同步或异步地监视。Groups提供了一个有用的同步机制,让代码可以基于其他task的完成。
2)Dispatch semaphores:一个
Dispatch semaphores和传统的semaphore类似,但是有更高的效率。
3)Dispatch Sources:一个dispatch source生成notification来响应特定类型的系统事件。你可以使用dispatch sources来监视事件,例如process notification、信号和descriptor events amony others。当一个事件发生时,dispatch source异步提交你的task code到特定的dispatch queue来处理。

3、使用blocks实现tasks:
4、创建和管理Dispatch Queue:
在你添加你的task到queue之前,你需要决定你要使用哪种类型的queue。
1)获得Global Concurrent Dispatch Queues:有3个Global queue,它们只是有不同的优先级。你不需要创建它们。使用dispatch_get_global_queue函数获得。
dispatch_queue_t aQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//DISPATCH_QUEUE_PRIORITY_HIGH、
DISPATCH_QUEUE_PRIORITY_LOW
注意:第2个参数为保留参数,现在只需要使用0即可。
这些global queue object你不需要retain或release(应该因为它们是全局的单例模式,retain和release都不会起作用)。

2)创建Serial Dispatch Queues:
dispatch_queue_t queue;
queue=dispatch_queue_create("com.example.MyQueue",NULL);
//第一个参数为queue name,第二个参数为queue attributes,是为将来保留使用的,现在使用NULL就可以了。

3)获得常用的Queue:
a)使用dispatch_get_current_queue函数来获得当前queue,一般用来作为debug使用。在一个block内部调用这个函数返回block被提交到的queue。在block外部调用这个函数返回默认的concurrent queue。
b)使用dispatch_get_main_queue函数返回主线程queue。
c)使用dispatch_get_global_queue函数来获得任意共享的concurrent queue。

4)Dispatch Queue的内存管理:
使用dispatch_retain和dispatch_release来管理。
即使你使用了垃圾收集系统,你也仍然需要retain和release你的dispatch queue和其他dispatch对象。GCD不支持垃圾收集模型。

5)存储当前context information with a Queue:
所有的dispatch对象(包括dispatch queues)允许你有一个相关的自定义的context数据。使用dispatch_set_context和dispatch_get_context函数来设置和读取。系统不使用你的context data,你需要自己allocate和deallocate它们。
对于queues,你可以使用context data来存储一个指针到一个Objective-C对象或其他数据结构来帮助逆识别queue或其他目的。你可以使用queue的销毁函数来deallocate你的context data。

6)提供queue的清理函数:
使用dispatch_set_finalizier_f函数来设置。相当于dealloc函数。例子:
void myFinalizerFunction(void *context){
  MyDataContext *theData=(MyDataContext *)context;
  //Clean up
  myCleanUpdataContextFunction(theData);
  //release 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_create_queue("com.example.CriticalTaskQueue",NULL);
  if(serialQueue){
    dispatch_set_context(serialQueue,data);
    dispatch_set_finalizer_f(serialQueue,&myFinalizerFunction);
  }
  return serialQueue;
}

7)添加tasks到queue:
a)添加一个单独的task到queue:
有两种方法添加:同步或异步。
异步使用dispatch_async和dispatch_async_f函数,一般都优先使用异步。
当你添加一个block对象到queue中后,你无法得知何时代码会执行。所以异步的添加block可以让你在添加的线程中继续做其他的工作。尤其是在主线程中操作时。
使用dispatch_sync和dispatch_sync_f函数来同步添加task。这会阻塞线程直到block执行完。

b)在一个task完成后执行一个completion block:
c)同时迭代循环:
例如,假设逆又一个for 循环如下:
for (i=0;i<count;i++){
  printf("%u\n",i)
}
如果每个迭代的工作都独立于其他迭代,而且迭代完成的顺序也不重要,你可以替换loop为 dispatch_apply或dispatch_apply_f函数。这个函数为每个单独的迭代循环 提交特定的block或函数到一个queue中,当dispatch到一个concurrent global queue中后,就有可能同时执行循环迭代。
注意:像一个常规的for loop,dispatch_apply和dispatch_apply_f函数会在所有的循环迭代完成后才返回。因此你应该小心。避免造成死锁(deadlock)和阻塞主线程。
例子:
dispatch_apply(count,queue,^(size_t i){
  printf("%u\n",i);
});//queue一般为一个global_queue

d)在主线程执行任务
使用dispatch_get_main_queue获得主线程,然后将其传递给dispatch_sync

8)挂起和回复Queues
你可以通过挂起来临时阻止一个queue执行block对象,使用dispatch_suspend函数和dispatch_resume函数。
调用dispatch_suspend使queue的suspension reference count加1,使用dispatch_resume函数减1.相当于retain和release。当reference count大于0时,queue就保持挂起状态。因此,你必须平衡使用suspend和resume。
重要通知:suspend和resume是异步的,而且只在不同的blocks之间生效。挂起一个queue不能使已经运行的block停止运行。

9)使用Dispatch Semaphores来调节有限资源的使用:
如果你的task要访问一些有限的(finite)资源,你可能就想使用一个dispatch semaphore来调整同时访问资源的task的数量。一个dispatch semaphore就像一个常规的semaphore一样工作。当资源可用时,它使用比获得一个传统的系统semaphore更少的时间来获得一个dispatch semaphore。这是因为GCD不需要call down into kernal for this particular case.
使用dispatch semaphore的要点:
a)当你使用dispatch_semaphore_create函数来创建semaphore时,你可以指定一个正整数值来指示resource可用的number值。
b)在每个task中,调用dispatch_semaphore_wait来等待semaphore
c)当wait调用返回时,获得资源并工作。
d)当完成后,释放掉资源并标志semaphore,使用dispatch_semaphore_signal函数。
例子:
//Create the semaphore,specifying the initial pool size
dispatch_semaphore_t fd_sema=dispatch_semaphore_create(getdtablesize()/2);

//wait for a free file descriptor
dispatch_semaphore_wait(fd_sema,DISPATCH_TIME_FOREVER);
fd=open("/etc/services",0_RDONLY);

//release the file descriptor when done
close(fd);
dispatch_semaphore_signal(fd_sema);

10)Waiting on Groups of Queued Tasks:
Dispatch groups用来在其他task执行完之前来堵塞一个线程。
使用dispatch_group_async函数来代替dispatch_async。
使用dispatch_group_wait函数来等待。
例子:
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);

11)Dispatch Queues and Thread Safety线程安全
a)Dispatch Queues本身是线程安全的。换句话说,你可以在任意线程中提交task到dispatch queue。
b)不要使用dispatch_sync函数提交task到
当前的queue中。这会造成死锁。
c)避免把使用带锁的任务提交到dispatch queue中,虽然你的task使用锁是安全的,但是当你获得一个lock时,你正在冒险将整个queue堵塞。类似地,对于concurrent queue,等待一个锁可能阻止其他任务执行。如果逆需要在你的代码中同步锁,那就使用一个serial dispatch queue来代替锁。
d)虽然你可以获得运行task的后台线程的信息,但是最好还是不要这么做。

//========================================================================
Grand Central Dispatch(GCD) Reference:只列出了常用的函数
1、创建和管理Queue
dispatch_get_global_queue
dispatch_get_main_queue
dispatch_queue_create
dispatch_get_current_queue
dispatch_queue_get_label

2、添加task到queue
dispatch_async、dispatch_async_f
dispatch_sync、dispatch_sync_f
dispatch_after
dispatch_apply
dispatch_once

3、使用Dispatch groups
dispatch_group_async
dispatch_group_create
dispatch_group_enter
dispatch_group_leave
dispatch_group_notify
dispatch_group_wait

4、管理Dispatch对象:
dispatch_debug
dispatch_get_context
dispatch_set_context
dispatch_release
dispatch_retain
dispatch_suspend
dispatch_resume
dispatch_set_finalizer_f

5、使用Semaphores:
dispatch_semaphore_create
dispatch_semaphore_signal
dispatch_semaphore_wait

6、类型
1)dispatch_block_t :添加到queue中的block原型
typedef void (^dispatch_block_t) (void);

2)dispatch_group_t:添加到一个queue的一组block
typedef struct dispatch_group_s *dispatch_group_t;

3)dispatch_object_t:一个多态类型,GCD dispatch object函数使用的。
typedef union {
 ....
} dispatch_object_t __attribute__((transparent_union));

4)dispatch_once_t:一个predicate,dispatch_once函数使用的
typedef long dispatch_once_t;

5)dispatch_queue_t:
typedef struct dispatch_queue_s *dispatch_queue_t;

7、常量
dispatch_queue_priority_t

#define DISPATCH_QUEUE_PRIORITY_HIGH        2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT     0
#define DISPATCH_QUEUE_PRIORITY_LOW         (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND  INT16_MIN

0 0
原创粉丝点击