ios中的多线程,除了可以用NSThread,还可以使用gcd。gcd的功能还是十分强大的,下面来详细了解一下。gcd是系统一个提供的一个线程队列,当我们需要使用多线程时,只要向已知的队列中添加任务。队列就会按照一定机制去执行这些任务。
队列分3类分别是:
连续队列: 顺序执行任务
并发队列:并发执行一个或者多个任务
主队列:它是应用程序中有效队列的主队列,执行的是应用程序中的主线程任务
首先看连续队列
dispatch_queue_t my_serial_queue;
my_serial_queue = dispatch_queue_create("com.apress.MySerialQueue1", NULL);
dispatch_block_t myBlock1=^{
NSLog(@"My block1 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
NSLog(@"My block1 end");
};
dispatch_block_t myBlock2=^{
NSLog(@"My block2 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];
NSLog(@"My block2 end");
};
dispatch_block_t myBlock3=^{
NSLog(@"My block3 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
NSLog(@"My block3 end");
};
dispatch_block_t myBlock4=^{
NSLog(@"My block4 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
NSLog(@"My block4 end");
};
dispatch_block_t myBlock5=^{
NSLog(@"My block5 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
NSLog(@"My block5 end");
};
NSLog(@"sync add start==================");
dispatch_sync(my_serial_queue, myBlock1);
NSLog(@"sync add my block1");
dispatch_sync(my_serial_queue, myBlock2);
NSLog(@"sync add my block2");
dispatch_sync(my_serial_queue, myBlock3);
NSLog(@"sync add my block3");
dispatch_sync(my_serial_queue, myBlock4);
NSLog(@"sync add my block4");
dispatch_sync(my_serial_queue, myBlock5);
NSLog(@"add my block5");
NSLog(@"sync add end==================");
NSLog(@"async add start==================");
dispatch_async(my_serial_queue, myBlock1);
NSLog(@"async add my block1");
dispatch_async(my_serial_queue, myBlock2);
NSLog(@"async add my block2");
dispatch_async(my_serial_queue, myBlock3);
NSLog(@"async add my block3");
dispatch_async(my_serial_queue, myBlock4);
NSLog(@"async add my block4");
dispatch_async(my_serial_queue, myBlock5);
NSLog(@"asyncadd my block5");
NSLog(@"async add end==================");
首先创建了一个连续队列
my_serial_queue = dispatch_queue_create("com.apress.MySerialQueue1",NULL);
第一个参数传入队列的名称,第二个是队列的属性,这里传入NULL
之后分别使用同步(dispatch_sync)和异步(dispatch_sync)添加任务,运行后的log如下:
2018-05-14 17:42:34.348926+0800 ocLearner[50012:1981077] sync add start==================
2018-05-14 17:42:34.349117+0800 ocLearner[50012:1981077] My block1 start
2018-05-14 17:42:39.352441+0800 ocLearner[50012:1981077] My block1 end
2018-05-14 17:42:39.352760+0800 ocLearner[50012:1981077] sync add my block1
2018-05-14 17:42:39.352935+0800 ocLearner[50012:1981077] My block2 start
2018-05-14 17:42:43.353401+0800 ocLearner[50012:1981077] My block2 end
2018-05-14 17:42:43.353689+0800 ocLearner[50012:1981077] sync add my block2
2018-05-14 17:42:43.353876+0800 ocLearner[50012:1981077] My block3 start
2018-05-14 17:42:46.355402+0800 ocLearner[50012:1981077] My block3 end
2018-05-14 17:42:46.355735+0800 ocLearner[50012:1981077] sync add my block3
2018-05-14 17:42:46.355946+0800 ocLearner[50012:1981077] My block4 start
2018-05-14 17:42:48.357490+0800 ocLearner[50012:1981077] My block4 end
2018-05-14 17:42:48.357802+0800 ocLearner[50012:1981077] sync add my block4
2018-05-14 17:42:48.357998+0800 ocLearner[50012:1981077] My block5 start
2018-05-14 17:42:49.358719+0800 ocLearner[50012:1981077] My block5 end
2018-05-14 17:42:49.359042+0800 ocLearner[50012:1981077] add my block5
2018-05-14 17:42:49.359220+0800 ocLearner[50012:1981077] sync add end==================
2018-05-14 17:42:49.359423+0800 ocLearner[50012:1981077] async add start==================
2018-05-14 17:42:49.359680+0800 ocLearner[50012:1981077] async add my block1
2018-05-14 17:42:49.359720+0800 ocLearner[50012:1981348] My block1 start
2018-05-14 17:42:49.359940+0800 ocLearner[50012:1981077] async add my block2
2018-05-14 17:42:49.360112+0800 ocLearner[50012:1981077] async add my block3
2018-05-14 17:42:49.360338+0800 ocLearner[50012:1981077] async add my block4
2018-05-14 17:42:49.360533+0800 ocLearner[50012:1981077] asyncadd my block5
2018-05-14 17:42:49.360913+0800 ocLearner[50012:1981077] async add end==================
2018-05-14 17:42:54.365384+0800 ocLearner[50012:1981348] My block1 end
2018-05-14 17:42:54.365683+0800 ocLearner[50012:1981348] My block2 start
2018-05-14 17:42:58.366416+0800 ocLearner[50012:1981348] My block2 end
2018-05-14 17:42:58.367190+0800 ocLearner[50012:1981348] My block3 start
2018-05-14 17:43:01.372527+0800 ocLearner[50012:1981348] My block3 end
2018-05-14 17:43:01.372879+0800 ocLearner[50012:1981348] My block4 start
2018-05-14 17:43:03.374228+0800 ocLearner[50012:1981348] My block4 end
2018-05-14 17:43:03.374553+0800 ocLearner[50012:1981348] My block5 start
2018-05-14 17:43:04.376980+0800 ocLearner[50012:1981348] My block5 end
可以看出,同步添加方法会等方法执行完成以后才返回,异步添加方法会直接返回,不需要等待任务是否完成。
那么如果是并发队列呢,来看代码
dispatch_queue_t my_queue;
my_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_block_t myBlock1=^{
NSLog(@"My block1 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
NSLog(@"My block1 end");
};
dispatch_block_t myBlock2=^{
NSLog(@"My block2 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:4.0]];
NSLog(@"My block2 end");
};
dispatch_block_t myBlock3=^{
NSLog(@"My block3 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
NSLog(@"My block3 end");
};
dispatch_block_t myBlock4=^{
NSLog(@"My block4 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
NSLog(@"My block4 end");
};
dispatch_block_t myBlock5=^{
NSLog(@"My block5 start");
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
NSLog(@"My block5 end");
};
NSLog(@"sync add start==================");
dispatch_sync(my_queue, myBlock1);
NSLog(@"sync add my block1");
dispatch_sync(my_queue, myBlock2);
NSLog(@"sync add my block2");
dispatch_sync(my_queue, myBlock3);
NSLog(@"sync add my block3");
dispatch_sync(my_queue, myBlock4);
NSLog(@"sync add my block4");
dispatch_sync(my_queue, myBlock5);
NSLog(@"sync add my block5");
NSLog(@"sync add end==================");
NSLog(@"async add start==================");
dispatch_async(my_queue, myBlock1);
NSLog(@"async add my block1");
dispatch_async(my_queue, myBlock2);
NSLog(@"async add my block2");
dispatch_async(my_queue, myBlock3);
NSLog(@"async add my block3");
dispatch_async(my_queue, myBlock4);
NSLog(@"async add my block4");
dispatch_async(my_queue, myBlock5);
NSLog(@"async add my block5");
NSLog(@"async add end==================");
首先看并发队列的创建
dispatch_queue_t my_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
第一个参数代表优先级,除了默认优先级外,还有DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW两个优先级。第二个参数都填0
和顺序队列的测试一样,也是分别用同步添加和异步添加的方法向队列添加任务,来看一下执行后的log
2018-05-15 10:08:41.423023+0800 ocLearner[50946:2026857] sync add start==================
2018-05-15 10:08:41.430928+0800 ocLearner[50946:2026857] My block1 start
2018-05-15 10:08:46.431787+0800 ocLearner[50946:2026857] My block1 end
2018-05-15 10:08:46.432072+0800 ocLearner[50946:2026857] sync add my block1
2018-05-15 10:08:46.432252+0800 ocLearner[50946:2026857] My block2 start
2018-05-15 10:08:50.433081+0800 ocLearner[50946:2026857] My block2 end
2018-05-15 10:08:50.433401+0800 ocLearner[50946:2026857] sync add my block2
2018-05-15 10:08:50.442155+0800 ocLearner[50946:2026857] My block3 start
2018-05-15 10:08:53.443781+0800 ocLearner[50946:2026857] My block3 end
2018-05-15 10:08:53.444092+0800 ocLearner[50946:2026857] sync add my block3
2018-05-15 10:08:53.444310+0800 ocLearner[50946:2026857] My block4 start
2018-05-15 10:08:55.445791+0800 ocLearner[50946:2026857] My block4 end
2018-05-15 10:08:55.446096+0800 ocLearner[50946:2026857] sync add my block4
2018-05-15 10:08:55.446321+0800 ocLearner[50946:2026857] My block5 start
2018-05-15 10:08:56.447314+0800 ocLearner[50946:2026857] My block5 end
2018-05-15 10:08:56.447538+0800 ocLearner[50946:2026857] sync add my block5
2018-05-15 10:08:56.447660+0800 ocLearner[50946:2026857] sync add end==================
2018-05-15 10:08:56.447759+0800 ocLearner[50946:2026857] async add start==================
2018-05-15 10:08:56.447870+0800 ocLearner[50946:2026857] async add my block1
2018-05-15 10:08:56.447884+0800 ocLearner[50946:2027008] My block1 start
2018-05-15 10:08:56.448008+0800 ocLearner[50946:2026857] async add my block2
2018-05-15 10:08:56.448053+0800 ocLearner[50946:2029055] My block2 start
2018-05-15 10:08:56.448167+0800 ocLearner[50946:2026857] async add my block3
2018-05-15 10:08:56.448201+0800 ocLearner[50946:2029056] My block3 start
2018-05-15 10:08:56.448348+0800 ocLearner[50946:2026857] async add my block4
2018-05-15 10:08:56.448381+0800 ocLearner[50946:2029057] My block4 start
2018-05-15 10:08:56.448775+0800 ocLearner[50946:2026857] async add my block5
2018-05-15 10:08:56.449061+0800 ocLearner[50946:2029058] My block5 start
2018-05-15 10:08:56.449233+0800 ocLearner[50946:2026857] async add end==================
2018-05-15 10:08:57.453500+0800 ocLearner[50946:2029058] My block5 end
2018-05-15 10:08:58.454006+0800 ocLearner[50946:2029057] My block4 end
2018-05-15 10:08:59.448899+0800 ocLearner[50946:2029056] My block3 end
2018-05-15 10:09:00.451476+0800 ocLearner[50946:2029055] My block2 end
2018-05-15 10:09:01.451462+0800 ocLearner[50946:2027008] My block1 end
可以看出,并发的队列,如果是同步添加,并没有实现并发,原因是如果是同步添加,必须等到添加的任务返回后才能继续往下执行。而如果是异步添加,异步队列,可以看出真正实现了并发运行。
第三个队列就是主队列,当我们需要必须在主线程下执行任务时,可以使用主队列,比如更新UI,代码如下:
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_block_t block1=^{
UILabel* label = [[UILabel alloc]init];
label.text=@"dispatch_main_queue";
label.textColor=UIColor.whiteColor;
label.frame=CGRectMake(50, 100, 150, 100);
[self.view addSubview:label];
};
dispatch_async(main_queue, block1);
正常情况下如果异步下载完资源,需要用主队列更新。这里为了简化,没有网络下载。仅仅演示了主队列的使用。
还有一个方法dispatch_get_current_queue方法可以获取当前的队列,但是这个方法已经被苹果废弃,因为有可能产生死锁。
下面来延时一下死锁的情况,上代码:
void deadfunc(dispatch_queue_t queue,dispatch_block_t block){
if(dispatch_get_current_queue() == queue){
NSLog(@"block");
block();
}else{
NSLog(@"dispatch block");
dispatch_sync(queue, block);
}
}
-(void)deadLockFunc{
dispatch_queue_t queueA = dispatch_queue_create("com.xiaoxiao.queueA", NULL);
dispatch_queue_t queueB = dispatch_queue_create("com.xiaoxiao.queueB", NULL);
dispatch_sync(queueA, ^{
dispatch_sync(queueB, ^{
dispatch_block_t block = ^{
NSLog(@"因为产生死锁,这句话用于得不到执行");
};
deadfunc(queueA,block);
});
});
}
可以看到当代码运行到dispatch_sysc(queue,block);以后将产生死锁,因为这段代码向queueA中同步添加了一个任务,儿代码段本身有在queueA的让一个任务中,所以,新添加的任务用于没有机会执行,因为现在任务需要等待先添加的任务结束才能继续往下运行。
如果想替代dispatch_get_current_queue,可以使用dispatch_queue_set_specific,但是其实个人觉得,这个方法如果使用不当,同样会死锁。因此,要特别关注同步添加的顺序队列的情况。好了,下面仅仅演示dispatch_queue_set_specific
-(void)specificFunc{
dispatch_queue_t queueA = dispatch_queue_create("com.xiaoxiao.queueA", NULL);
//dispatch_queue_t queueB = dispatch_queue_create("com.xiaoxiao.queueB", NULL);
//dispatch_set_target_queue(queueB, queueA);
static int specificKey;
CFStringRef specificValue = CFSTR("queueA");
dispatch_queue_set_specific(queueA,&specificKey,(void*)specificValue,(dispatch_function_t)CFRelease);
dispatch_sync(queueA, ^{
dispatch_block_t block = ^{
};
CFStringRef retrievedValue = dispatch_get_specific(&specificKey);
if (retrievedValue) {
NSLog(@"block");
block();
} else {
NSLog(@"dispatch block");
dispatch_sync(queueA, block);
}
});
}
其实就是给队列添加了一个标记。在需要的时候可以判断是否是特定的队列。
最后再说以队列的上下文吧,因为有些情况下需要传入参数。
传入上下文的方法是:
dispatch_set_context(dispatch_object_t object,void *_Nullable context)
第一个参数是队列对象,第二个参数是C格式的void* 类型指针
下面分别给出上下文是C格式数据和OC格式数据的例子:
typedef struct _Data{
int number;
} Data;
void cleanStaff(void* context){
NSLog(@"In clean ,context number:%d",((Data*)context)->number);
free(context);
}
/**
传入C数据的上下文
*/
-(void)testBody1{
dispatch_queue_t queue = dispatch_queue_create("com.xiaoxiao.queue1", NULL);
Data *myData = malloc(sizeof(Data));
myData->number=10;
dispatch_set_context(queue, myData);
dispatch_set_finalizer_f(queue, cleanStaff);
dispatch_async(queue,^{
Data *data = dispatch_get_context(queue);
NSLog(@"1:context number:%d",data->number);
data->number = 20;
});
}
void myFinalizerFunction(void *context){
NSLog(@"myFinallizerFunction");
NSMutableDictionary *theData = (__bridge_transfer NSMutableDictionary*)context;
[theData removeAllObjects];
}
可以看到,dispatch_set_context可以设置上下文,dispatch_get_context可以获得之前设置的上下文。另外可以通过dispatch_set_finalizer_f来设置清理函数,当队列销毁的时候,也会调用注册的销毁函数来销毁上下文。
如果上下文是OC对象的化:
void myFinalizerFunction(void *context){
NSLog(@"myFinallizerFunction");
NSMutableDictionary *theData = (__bridge_transfer NSMutableDictionary*)context;
[theData removeAllObjects];
}
-(void)testBody2{
dispatch_queue_t queue = dispatch_queue_create("com.xiaoxiao.queue2", NULL);
NSMutableDictionary *myContext = [[NSMutableDictionary alloc]initWithCapacity:5];
[myContext setObject:@"My context" forKey:@"title"];
[myContext setObject:[NSNumber numberWithInt:0] forKey:@"value"];
dispatch_set_context(queue, (__bridge_retained void*)myContext);
dispatch_set_finalizer_f(queue,myFinalizerFunction);
dispatch_async(queue, ^{
NSMutableDictionary *myContext = (__bridge NSMutableDictionary*)dispatch_get_context(queue);
NSString* title = [myContext objectForKey:@"title"];
NSNumber* value = [myContext objectForKey:@"value"];
NSLog(@"title:%@,value:%d",title,[value intValue]);
});
}
如果上下文是oc对象,可以看到,在dispatch_set_context的时候需要用__bridge_retained,表示移交了内存管理权,相当于对void* 对象retained了,__bridge_transfer表示c对象释放管理权,把内存控制交给ARC系统。
好了,上下文系统就这些。先写到这里吧