IOS并发编程——Grand Center Dispatch
来源:互联网 发布:oraclesql优化技巧 编辑:程序博客网 时间:2024/06/03 19:24
并发编程往往能够提高程序的效率,在其他平台中进行并发编程往往就是多线程的编程,在IOS中同样可以进行多线程编程,但是Apple的官方文档却告诉我们,尽量不要使用原生线程,而是使用其他替代技术。为什么呢?有如下几点理由:
1、原生线程编程往往需要涉及同步,线程资源获取释放等操作,相对复杂。
2、原生多线程编程线程切换运行由人为控制,不如直接交给操作系统来管理线程效率高(操作系统会根据系统实时状况灵活操作多线程)。
3、每一次对原生线程的操作,都要进行内核层的操作。而内核层操作花费时间大。而由操作系统代劳的并发操作只会在必要时机切换到内核层。
OK,既然将并发操作交给系统有那么多好处,那我们就来了解一下IOS系统的并发编程。
IOS并发编程技术 包括:
- Dispatch Queues
- Dispatch Source
- Operation Queues
什么是GCD
block object(task block)
int x = 123;int y = 456; // Block declaration and assignmentvoid (^aBlock)(int) = ^(int z) { printf("%d %d %d\n", x, y, z);}; // Execute the blockaBlock(789); // prints: 123 456 789
block object接受的参数
NSLog(@"%d", &a); dispatch_sync(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ NSLog(@"%d", &a); }); NSLog(@"%d", &a);
输出:
int a = 13; NSLog(@"%d", &a); dispatch_sync(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ NSLog(@"%d", &a); }); NSLog(@"%d", &a);
关于block设计的指导方针
Dispatch Queues
Dispatch queue分类
获取Dispatch queue
获取系统dispatch queue
dispatch_get_global_queue
dispatch_queue_t dispatch_get_global_queue ( long identifier, unsigned long flags );
该函数会返回一个系统全局的concurrent queue,因为该queue是系统管理的,因此我们调用
dispatch_suspend
, dispatch_resume
, 或 dispatch_set_context等函数操作该queue时,是无效的。
dispatch_get_main_queue
创建自己的Dispatch queue
可以通过函数
dispatch_queue_create
dispatch_queue_t dispatch_queue_create ( const char *label, dispatch_queue_attr_t attr );
创建自己的Dispatch queue。label为Dispatch queue名称,而attr可以设置为
DISPATCH_QUEUE_SERIAL
(or NULL
)创建serial queue,DISPATCH_QUEUE_CONCURRENT
来创建concurrent queue。Dispatch queue的内存管理
除了上述系统管理的global queue与main queue不需要我们操心外,我们需要对自己创建的queue进行引用计数管理。即使在我们在创建具有垃圾回收功能的程序,而dispatch queue不支持垃圾回收机制。dispatch queue的计数管理函数如下:
dispatch_retain
dispatch_release
在dispatch queue中存储自定义信息
我们可以通过调用函数dispatch_set_context
dispatch_get_context
在dispatch queue中设置,提取自定义数据。系统并不会理会这些数据,因此,我们需要手动来分配释放或解引用OC对象。而这一个操作,可以放在dispatch queue的finalizer function中执行(类似于类的析构函数)。
Dispatch queue的finalizer function
对于dispatch queue,我们可以设置finalizer function,让Dispatch queue的引用计数为0时,自动调用该函数。注意,仅当我们为Dispatch queue设置了自定义数据时,finalizer 函数才会被调用。设置Dispatch queue自定义数据及finalizer 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;}
将task block提交到Dispatch queue中
同步提交
dispatch_sync
dispatch_sync_f
异步提交
dispatch_async
dispatch_async_f
当然,我们应优先使用异步提交到Dispatch queue,因为同步提交会使我们的线程阻塞直到提交的block完成为止。同时,对于同步提交,可能会造成死锁现象,如在提交的Dispatch serial queue中的task block中再次调用同步提交block到同一个Dispatch serial queue,则必定会造成死锁。对于concurrent queue,Apple官方文档也不建议在同一个queue中调用同步提交函数。
关于GCD的其他
利用并发queue优化循环操作
Apple文档还提到,当我们在使用循环时,如果每次循环的结果间是相互独立并且循环的执行顺序没有要求,那么可以使用
dispatch_apply
dispatch_apply_f
函数来优化循环操作。这两个函数会根据循环次数,提交若干block或函数到指定的concurrent queue中,这样,每个循环逻辑就能够并发的执行了。官方示例如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(count, queue, ^(size_t i) { printf("%u\n",i);});
当然,这种优化并不是一定的,毕竟操作queue也是需要系统开销的,应根据具体情况而定。
Queue Task的内存管理
Queue Task自带autorelease pool,因此所有的OC对象均会自动释放,但系统并不保证何时会自动释放这些对象。因此,如果我们的程序对内存使用还是比较敏感,应自己再写需要的autorelease pool。
暂停与恢复Queue
我们可以控制Dispatch queue的暂停与恢复,它们分别对应如下函数:
dispatch_suspend
dispatch_resume
上面两个函数会对应的增加或减少Dispatch queue的暂停引用计数。因此,要让Dispatch queue恢复运行,还需要调用与dispatch_suspend
相对应次数的dispatch_resume
函数。
通过Dispatch Semaphores来代替传统的Semaphores
通过Dispatch Semaphores来代替传统的Semaphores
类似于传统信号量,我们也可以通过Dispatch Semaphores来对于关键资源的访问进行控制。但是GCD使用的Semaphores更为高效,因为GCD仅当真正需要阻塞线程来等待信号量时才会真正与系统内核层交互,减少了系统消耗。 系统运用dispatch semaphores步骤如下:
When you create the semaphore (using the
dispatch_semaphore_create
function), you can specify a positive integer indicating the number of resources available.In each task, call
dispatch_semaphore_wait
to wait on the semaphore.When the wait call returns, acquire the resource and do your work.
When you are done with the resource, release it and signal the semaphore by calling the
dispatch_semaphore_signal
function.
// Create the semaphore, specifying the initial pool sizedispatch_semaphore_t fd_sema = dispatch_semaphore_create(getdtablesize() / 2); // Wait for a free file descriptordispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);fd = open("/etc/services", O_RDONLY); // Release the file descriptor when doneclose(fd);dispatch_semaphore_signal(fd_sema);
使用起来还是比较简单的。
利用Dispatch group来等待Dispatch queue中的任务结束
利用Dispatch group来等待Dispatch queue中的任务结束
有时候,我们的下一步操作需要上一步任务的结果,但在并发执行的情况下,我们就需要在程序的某个点停下来,等待或询问那个并发执行的任务是否已经完成。对于提交到concurrent queue中的任务,IOS提供了Dispatch group
来进行这种等待或询问。
官方实例如下:
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 groupdispatch_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);
利用Dispatch group进行等待,一般步骤如下
1、同往常一样,获取concurrent queue。
2、创建Dispatch group对象。
3、通过
dispatch_group_async
函数,将任务提交到Dispatch queue中,同时,将task,dispatch queue与dispatch group相联系起来。4、在适当的时刻,调用
dispatch_group_wait
函数等待提交到dispatch queue中的任务完成。Dispatch queue与线程安全
Dispatch queue与线程安全
虽然Dispatch queue由系统控制,但是对于dispatch queue的线程安全,Apple还是希望你知道如下几点:
1、Dispatch queue本身是线程安全的。即是说,你可以在多个线程中同时操作dispatch queue,而不必进行线程同步操作。系统会为你代劳。
2、如前面提到过的,不要在同一个serial queue中调用dispatch_sync函数提交任务,这显而易见会造成死锁。
3、尽量不要在提交的任务中使用锁,这样会降低dispatch queue的效率。可能的话考虑使用 serial queue来代替锁。
4、最后一条不是很理解是为什么,先抄录在这里吧
Although you can obtain information about the underlying thread running a task, it is better to avoid doing so. For more information about the compatibility of dispatch queues with threads, seeCompatibility with POSIX Threads.
参考文献:
https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW23
- IOS并发编程——Grand Center Dispatch
- iOS并发编程NSThread、Grand Central Dispatch、Operation Queue
- 关于GCD(Grand Center Dispatch)
- iOS学习之——GCD(Grand Central Dispatch)
- GCD(Grand Center Dispatch)使用详解
- 并发 (二):Grand Central Dispatch
- iOS多线程编程之Grand Central Dispatch(GCD)
- IOS学习之Grand Central Dispatch(GCD)编程基础
- iOS多线程编程(三)Grand Central Dispatch(GCD)详解
- iOS多线程编程之Grand Central Dispatch(GCD)
- iOS多线程编程之Grand Central Dispatch(GCD)
- iOS多线程编程(三)Grand Central Dispatch(GCD)详解
- iOS 多线程编程之Grand Central Dispatch(GCD)
- iOS多线程编程之Grand Central Dispatch(GCD)
- iOS Grand Central Dispatch(GCD)
- IOS GCD ---- Grand Central Dispatch
- <iOS>Grand Central Dispatch简介
- iOS并发编程指南 2 -- Dispatch Queues
- 网页开发之字体(一)
- Deep Learning 的一些认识
- python微框架Bottle(http)
- 守护线程和用户线程
- 11gR203 add node for RAC
- IOS并发编程——Grand Center Dispatch
- Fiddler模拟post提交,参数为空的解决办法
- SEO搜索引擎优化总结
- Spring技术内幕:Spring AOP的实现原理(五)
- 如果VMware打开系统报内部错误,很简单就解决啦!
- Unicode、UTF-8 和 ISO8859-1到底有什么区别
- android SDK更新慢的解决办法
- 二叉树的建立与遍历(二)(c++实现)
- mongodb安装