最近比较清闲,写一下多线程GCD。
多线程如果使用不当,很容易引起死锁、数据竞争、因线程太多消耗过多的内存和因增加了cpu处理压力造成电量消耗过快等问题,虽然要回避这些问题有许多方法,但是很多都偏于复杂,尽管极易发生很多问题,很多时候也需要使用多线程开发,因为多线程可以保证应用的响应性能,例如:执行时间较长的处理仍然可以保证用户的界面响应。
我们先说GCD的队列(Queue),队列分为并发队列(Concurrent)和串型队列(Serial)。
Dispatch Queue 队列
DISPATCH_QUEUE_SERIAL
在Serial队列里面,使用的是一个线程,顺序执行你的操作。但是可以同时创建多个Serial队列,让多个Serial同时执行。但是我们尽量不要使用DISPATCH_QUEUE_SERIAL生成多个线程,因为这样容易引起数据竞争等问题。
DISPATCH_QUEUE_CONCURRENT
在Concurrent队列里面,使用的是多个线程,执行的你的代码块的时候,A代码块执行,不管你A块又没有执行完毕,B块都开始执行,也就是使用多个线程同时执行多个处理。
dispatch_queue_create
通过dispatch_queue_create可以创建线程队列,例如:
dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
这个创建的线程队列对象名字叫 queue,是一个并发队列,如果将他加入一段代码,执行的顺序是这样的
dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
/*
执行耗时操作*/
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"%ld", (long)i);
}
});
/*
响应其他处理*/
NSLog(@"在队列queue未执行完毕时候,就已经开始执行这个打印");
上述代码打印的结果是:
Main Dispatch Queue
Main Dispatch Queue是主线程中执行的线程队列,其获取方法是系统已经预置好的dispatch_get_main_queue()。
这个线程的主要用于,当开启多个线程处理耗时操作,当其中一个耗时操作处理完毕需要刷新UI时,就需要在dispatch_get_main_queue()中刷新,因为UI只能在主线程中刷新。
获取方法:
dispatch_queue_t main = dispatch_get_main_queue();
Global Dispatch Queue
Global Dispatch Queue是Concurrent类型线程队列,Global Dispatch Queue 有4个优先级,如下:
#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 /*后台优先级*/
虽然Global有4个优先级,但是根据以往经验和查阅的资料来判断,这4个优先级并不能保证实时性,只能作为大致、大概的判断,比如这个处理的内容要求尽可能的最先处理完毕,那么我们就使用DISPATCH_QUEUE_PRIORITY_HIGH
,如果处理的内容可有可无,则可以用DISPATCH_QUEUE_PRIORITY_BACKGROUND
。
示例如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*
执行耗时操作*/
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"%ld", (long)i);
}
/*
进入主线程,例如刷新UI*/
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"可以执行主线程操作");
});
});
打印结果如图2
dispatch_after
dispatch_after可以执行指定时间的操作,在多少时间以后执行操作,所以他有以下四个时间单位
SEC:秒
MSEC:毫秒
USEC:微秒
NSEC:纳秒
PER:每
#define NSEC_PER_SEC 1000000000ull /*每秒有多少纳秒,使用的时候,这个就是最常用的秒*/
#define NSEC_PER_MSEC 1000000ull /*每毫秒有多少纳秒*/
#define USEC_PER_SEC 1000000ull /*每秒有多少微秒。(注意是指在纳秒的基础上)*/
#define NSEC_PER_USEC 1000ull /*每微秒有多少纳秒*/
获取时间的节点有两个
#define DISPATCH_TIME_NOW (0ull) /*当前*/
#define DISPATCH_TIME_FOREVER (~0ull) /*在遥远的未来*/
值得注意的是,Main Dispatch Queue是在主线程的RunLoop执行的,所以在dispatch_after
线程Block最快要在5秒执行(假设我们设置的dispatch_time_t 是5秒),但是可能因为主线程因有大量的处理或者主线程本身就有以下延时操作,那么等待的时间将会更长。
代码示例:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"hh:mm:ss:SSS"];
NSDate *datenow = [NSDate date];
NSString *nowtimeStr = [formatter stringFromDate:datenow];
NSLog(@"now = %@",nowtimeStr);
/*延时操作*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSDate *dateNow2 = [NSDate date];
NSString *nowtimeStr2 = [formatter stringFromDate:dateNow2];
NSLog(@"now2 = %@",nowtimeStr2);
});
根据打印结果可以得知,延时操作并不是绝对的5秒,虽然如此,但是如果只是想大致的执行延时处理,这个函数还是可以的,打印结果如图3。
Dispatch Group
Group线程是当我们指定几个线程,可以是异步的耗时操作,当几个耗时操作执行完毕后再执行。
先说一下几个关键函数:
/*Group线程对象*/
dispatch_group_t;
/*生成Group线程对象的方法,使用方法和dispatch_get_main_queue()一样*/
dispatch_group_create(void);
/*该函数与dispatch_async函数相同,但是dispatch_group_async有两个参数,第一个要填写创建的dispatch_group_t类型的对象,第二个dispatch_queue_t类型的对象*/
void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
/*该函数是当所有的dispatch_group_async函数执行完毕后,通知到这个函数,然后执行block里面的代码块,需要填写两个参数,第一个填写dispatch_group_t类型,第二个dispatch_queue_t类型的对象*/
void dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
/*dispatch_group_wait需要填写两个参数,第一个参数dispatch_group_t类型,第二个参数参考dispatch_after线程的时间参数,如果dispatch_time_t类型填写成DISPATCH_TIME_FOREVER,那么就会等待所有的dispatch_group_async线程执行完毕才会执行判断,如果填写DISPATCH_TIME_FOREVER不如使用dispatch_group_notify函数*/
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
以下分别是dispatch_group_notify
和dispatch_group_wait
两个函数的代码示例,具体使用根据业务和需求使用。
/*dispatch_group_notify示例*/
/*创建Dispatch Group线程*/
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5; i++) {
NSLog(@"--------1-------- %@",[NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10; i++) {
NSLog(@"--------2-------- %@",[NSThread currentThread]);
}
});
/*当1,2完成后再执行3*/
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--------3-------- %@",[NSThread currentThread]);
/*打印结果图4*/
});
/*dispatch_group_wait示例*/
/*创建Dispatch Group线程*/
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5; i++) {
NSLog(@"--------1-------- %@", [NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10; i++) {
NSLog(@"--------2-------- %@", [NSThread currentThread]);
}
});
/*设置时间为1秒钟*/
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
/*在1秒钟内,1和2全部执行完毕得到long值为0*/
/*打印结果图5*/
if (!result) {
NSLog(@"dispatch_group_async全部执行完毕 %@", [NSThread currentThread]);
} else {
NSLog(@"dispatch_group_async还有在执行的,但是等到时间到了 %@", [NSThread currentThread]);
}
/*将循环次数增大,增加循环打印时间超过1秒的情况*/
/*创建Dispatch Group线程*/
dispatch_group_t group = dispatch_group_create();
/*将循环次数增大*/
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5000; i++) {
NSLog(@"--------1-------- %@", [NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10000; i++) {
NSLog(@"--------2-------- %@", [NSThread currentThread]);
}
});
/*设置时间为1秒钟*/
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
/*在1秒钟内,1和2没有执行完毕得到long值为>0*/
/*打印结果图6*/
if (!result) {
NSLog(@"dispatch_group_async全部执行完毕 %@", [NSThread currentThread]);
} else {
NSLog(@"dispatch_group_async还有在执行的,但是等到时间到了 %@", [NSThread currentThread]);
}