标签:gcd 并行处理 concurrent api 多线程
GCD(Grand Dispatch)是异步执行的技术之一
下面这个例子就是在后台线程中执行长时间处理,主线程使用该处理结果
dispatch_async(dispatch_get_global_queue(0, 0), ^{
/*
长时间处理 比如 AR用画像识别
数据库访问等
长时间处理完毕,主线程使用该处理结果
*/
NSLog(@"后台处理");
dispatch_async(dispatch_get_main_queue(), ^{
//只在主线程可以的处理
//例如用户界面更新
NSLog(@"主线程处理");
});
});
/*
在导入GCD之前,Cocoa框架使用NSObject类的performSelectorInBackground:withObject实例方法和PerformSelectorOnMainThread实例方法等简单的多线程编程技术。
下面用这两种方法实现上面的GCD
*/
//执行后台线程
-(void)lauchThreadByObject_performSelectouInBackground_withobject
{
[self performSelectorInBackground:@selector(doWork) withObject:nil
];
}
//后台线程处理
-(void)doWork
{
/*
长时间处理 比如 AR用画像识别
数据库访问等
长时间处理完毕,主线程使用该处理结果
*/
[self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
}
//主线程处理方法
-(void)doneWork
{
//只在主线程中处理,例如用户界面刷新等
}
//上面的方法确实是比用NSThread类进行多线程简单,但确实没有GCD更加方便和简单。而且还可以通过GCD提供的系统级线程管理提高执行效率。
/*
多线程编程
由于使用多线程程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此看上去就像1个CPU核能够并列的执行多个线程一样,而且在具有多个CPU核的情况下,就是真的提供了多个CPU核并行执行多个线程的技术。
但是多线程实际上是一种容易发生各种问题的编程技术。
比如多个线程更新相同的资源,会导致数据的不一致,(数据竞争)停止等待时间的线程会导致多个线程相互持续的等待(死锁)使用多线程会消耗大量内存等。
尽管存在问题,也应当使用多线程编程,因为多线程编程保证应用程序的响应性能
*/
//GCD的API
//Dispatch Queue
/* dispatch_async(queue, ^{
//想要执行的任务
});
开发者要做的只是定义想要执行的任务并追加到适当的Dispatch Queue中
Dispatch Queue 按照追加的顺序(FIFO)执行处理
另外在执行处理时存在两种Dispatch Queue,一种等待现在执行中处理的Serial(连续的) Dispatch Queue,另一种是不等待现在执行处理的 Concurrent(并发的) Dispatch Queue
*/
//1.Dispatch_Queue_create
//生成serial Dispatch Queue 时
dispatch_queue_t mySerialDispatchQueue=dispatch_queue_create("com.example.gcd.mySerialDispatchQueue", NULL);
//理论上来说,用该函数可以生成任意多个Dispatch Queue 但是前面提到,过多使用多线程,会产生过多问题。 当为了比避免多线程问题之一:数据竞争就可以使用Serial Dispatch Queue 当想并行执行不会发生数据竞争等问题时,使用Concurrent Dispatch Queue而且对于其来说,不管生成来说,由于XNU内核只是用有效管理的线程,因此不会发生serial Dispatch Queue等问题
//生成Concurrent Dispatch Queue
dispatch_queue_t myConCurrentDispatchQueue= dispatch_queue_create("com.example.gcd.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myConCurrentDispatchQueue, ^{
NSLog(@"block on myConcurrentDispatchQueue");
});
//2.Main Dispatch Queue/global Dispatch Queue
/*MAin Dispatch Queue 正如其名称一样,是在主线程中执行的Dispatch Queue,因为主线程只有一个,因此Main Dispatch Queue自然就是 serial Dispatch Queue 追加到Main Dispatch Queue 的处理在主线程的runloop中执行,因此要将用户界面更新等一些不许在主线程中执行的处理追加到Main Dispatch Queue中执行。这与NSObject 类的performSelectorOnMainThread该势力方法相同。
Global Dispatch Queue是所有应用程序都能够使用的Concurrent Dispatch Queue 只要获取global Dispatch Queue使用即可,其有四个优先级,高优先级(high Priority)默认优先级(default priority),低优先级(low priority),后台优先级(Background Priority)
*/
/*
3.dispatch_set_target_queue
该函数生成的Dispatch Queue 不管是serial Dispatch Queue还是concurrent Dispatch Queue 都使用默认优先级。
在必须将不可并行执行处理追加到多个serial Dispatch Queue,如果使用Dispatch_set_target_queue函数将目标制定为某一个serial Dispatch Queue,即可防止并行执行
*/
4.dispatch_after
延迟处理
*/
dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 2ull*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds");
});
//上述ull表示unsigned long long或者
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds");
});
//注意Dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue。因此上述可能代码可能3s+1/60s,可能更长。Dispatch time函数通常用于计算相对时间,而Dispatch_walltime 用于计算绝对时间。
//5.dispatch Group 在追加到Dispatch Queue 中得多个处理全部结束后,想执行结束处理 只使用一个serial Dispatch Queue 时,只要将想执行的处理全部追加到serial Dispatch Queue中并在最后追加结束处理,即可实现。但是在使用Concurrent Dispatch Queue时或者同时使用多个Dispatch 时,源码就会复杂 这种情况下,我们就用group
//追加3个block到global Dispatch Queue中,这些block执行完毕后,就会执行Main Dispatch Queue 中结束处理使用的block
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group =dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");});
//最后一句的第一个参数指定为要监视的Dispatch group 在追加到Dispatch group的全部处理结束后,将第三个参数的block追加到第二个参数的Dispatch Queue,在Dispatch_group_notify函数中不管指定什么样的Dispatch Queue属于Dispatch group 的全部处理在追加指定的block时都已执行结束。
//由于多个线程并行执行,因此执行顺序不定,执行会发生变化,但是done一定是最后输出 但是无论向什么样的Dispatch Queue 中追加处理,使用Dispatch group都可以监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到Dispatch Queue中,这就是使用Dispatch group的原因。
//6.另外Dispatch group中也可使用Dispatch_group_wait函数仅等待全部处理执行结束
dispatch_queue_t queuewait=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t groupwait =dispatch_group_create();
dispatch_group_async(groupwait, queuewait, ^{NSLog(@"blk1");});
dispatch_group_async(groupwait, queuewait, ^{NSLog(@"blk2");});
dispatch_group_async(groupwait, queuewait, ^{NSLog(@"blk3");});
dispatch_group_wait(groupwait, DISPATCH_TIME_FOREVER);
//dispatch_group_wait参数指定为等待的时间(超时),它属于Dispatch_time_t类型的值。该代码用forever,意思为永久等待。只要Dispatch group的处理尚未执行结束,就会一直等待。中途不能取消。
//等待间隔为1s时
dispatch_time_t time1=dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
long result=dispatch_group_wait(groupwait, time1);
if (result==0) {
NSLog(@"Dispatch group全部处理结束");
}else{
NSLog(@"属于Dispatch的某一个处理还在进行中");
}
//推荐使用Dispatch_group_notify
//7.在访问数据库或者文件时,可以使用serial Dispatch Queue避免数据竞争的问题。
//当然写入处理确实不可与其他的写入处理或者包含读取处理的其他某些处理并行处理,但是如果读取处理至于读取处理并行执行,那么多个并行执行就不会发生问题。即为了高效率的进行访问,读取处理追加到Concurrent Dispatch Queue中,写入处理在任何一个读取处理没有执行的状态下,追加到serial Dispatch Queue中即可。(在写入处理结束之前,读取处理不可执行)。
//我们用Dispatch_barrier_async函数,该函数同Dispatch_queue_create函数生成的concurrent Dispatch Queue一起使用。
//首先Dispatch_queue_create函数生成Concurrent Dispatch Queue,在Dispatch_async中追加处理。
dispatch_queue_t queuebarrier=dispatch_queue_create("com.example.gcd.forBarrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queuebarrier, ^{NSLog(@"blk0_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk1_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk2_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk3_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk4_for_reading");});
dispatch_barrier_async(queuebarrier, ^{NSLog(@"blk_for_writting");});
dispatch_async(queuebarrier, ^{NSLog(@"blk5_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk6_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk7_for_reading");});
dispatch_async(queuebarrier, ^{NSLog(@"blk8_for_reading");});
//Dispatch_barrier_aysync函数会等待追加到concurrent Dispatch Queue上得并行执行的全部处理结束之后,在将制定的处理追加到该concurrent Dispatch Queue中,然后在Dispatch Queue才恢复一般的动作,追加到该Concurrent Dispatch Queue的处理又开始并行执行
//使用Concurrent Dispatch Queue和Dispatch_barrier_aysnc 函数可执行高效率的数据库访问和文件访问。
//8.Dispatch_sync 意味着非同步,就是将block非同步的追加到制定的Dispatch Queue中 其不做任何等待。
//下面我们执行Main Dispatch Queue时,使用另外的县城global Dispatch Queue进行处理,处理结束后立即使用所得到的结果,这种情况下,可以使用Dispatch_sync函数
dispatch_queue_t queuesync=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queuesync, ^{NSLog(@"处理");});
//Dispatch_sync可以简化源代码,但是也极易引起代码,死锁
/*
9. dispatch_apply 函数是将dispatch_sync函数和dispatch_group关联的API,该函数按照指定的次数将指定的block追加到dispatch queue中,并等待全部处理结束。
第一个参数为重复次数,第二个参数为追加对象的dispatch queue,第三个处理为追加的处理。
*/
dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index){
NSLog(@"%zu",index);
});
NSLog(@"done");
NSArray *array=[[NSArray alloc]initWithObjects:@"1",@"2",@"3", nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//将nsarray的类对象的所有元素执行处理。
dispatch_apply([array count], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index){
//并列处理包含在nsarray对象的全部对象。
NSLog(@"%zu: %@",index,[array objectAtIndex:index]);
});
//dispatch_apply函数中的全部处执行结束
//在Main dispatch queue中非同步执行。
dispatch_async(dispatch_get_main_queue(), ^{
//在 Main dispatch queue 中执行处理 用户界面更新等
});
});
/*
10. dispatch_suspend/dispatch_resume
当追加大量处理到dispatch_queue时,在追加处理过程中,有时希望不执行已经追加的处理,
这种情况下,可以挂起 dispatch queue即可,当可以执行时在恢复。
dispatch_suspend(queue);
//恢复指定的dispatch queue
dispatch_resume(queue)
*/
/* 11. dispatch_semaphore
因为并行处理更新数据时,会产生数据不一致的情况,可以使用serial dispatch queue和dispatch_barrier_aysnc可避免这类问题,但是可以进行更加细粒度的排他控制
但暂时用不到,不做介绍、
*/
/*
dispatch_once 保证在应用程序执行中只执行一次指定处理的API。
*/
//12原来用来进行初始化的源码。在多核CPU的情况下,在正在更新表示是否初始化的标志时,可能进行多次初始化处理
static int initialized=NO;
if (initialized==NO) {
//初始化
initialized=YES;
}
//如果使用dispatch_once 函数,则源码可以写为
static dispatch_once_t pred;
dispatch_once(&pred,^{
//初始化
});
//该代码在多线程情况下,也可以保证百分百安全。 这就是所说的单例模式。
//13dispatch I/O可以提高文件的可读性,暂不做介绍。
本文内容是学习高级Object-c编程一书所写
标签:gcd 并行处理 concurrent api 多线程
原文地址:http://blog.csdn.net/see_you_in_the_past/article/details/44775707