本文主要是介绍IOS并发编程——Grand Center Dispatch,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
并发编程往往能够提高程序的效率,在其他平台中进行并发编程往往就是多线程的编程,在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 assignment
void (^aBlock)(int) = ^(int z) {printf("%d %d %d\n", x, y, z);
};// Execute the block
aBlock(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_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 structuremyCleanUpDataContextFunction(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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!