GCD:
1,主队列,主队列创建的任务是主线程任务。
主线程只能用于异步队列,否则会造成死锁,主队列是放在主线程队列当中,如果用同步,会造成主线程多个任务同时等待彼此任务,导致死锁,例如:
dispatch_queue_t mainQueue = dispatch_get_main_queue(); //sync,同步会导致死锁 dispatch_sync(mainQueue, ^{ NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); //所以正确的为: dispatch_queue_t mainQueue = dispatch_get_main_queue(); //sync,同步会导致死锁 dispatch_async(mainQueue, ^{ NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); });
2,全局队列,也是并发队列,其创建方式为:
dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(gQueue, ^{ NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); });
两个参数,第一个参数是优先级,根据优先级优先访问当前队列,第二个值苹果规定为0,否则会返回空队列。
首先,如果全局队列执行异步操作,系统会自动创建一个子线程去执行当前异步任务,如上代码,输出的为:
dispatch_get_main_queue - - - <NSThread: 0x600002558c40>{number = 5, name = (null)} - - - 0
如果全局队列执行同步操作,系统不会创建新的线程,会在主线程中执行当前操作,如下:
dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_sync(gQueue, ^{ NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); //输出为: dispatch_get_main_queue - - - <NSThread: 0x600003e87e00>{number = 1, name = main} - - - 1 //显示为主线程
3,用户队列,也就是自定义队列,其创建方式为:
dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL); dispatch_async(userQueue, ^{ NSLog(@"dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); });
上述代码中执行了异步操作,所以系统会自动创建一个子线程去执行当前用户队列中的任务,如下输出:
dispatch_queue_create - - - <NSThread: 0x600002790900>{number = 5, name = (null)} - - - 0 //表明当前线程不是主线程,是系统自动创建的子线程
如果是同步操作,如下:
dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL); dispatch_sync(userQueue, ^{ NSLog(@"dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); //输出,显示为主线程 dispatch_queue_create - - - <NSThread: 0x600000415380>{number = 1, name = main} - - - 1
用户队列分为并发队列和非并发队列,具体创建的时候根据传递的参数决定,DISPATCH_QUEUE_CONCURRENT,并发队列;
DISPATCH_QUEUE_SERIAL,非并发(串行队列)队列;
并发队列执行异步操作,会创建子线程;并发队列执行同步操作,不会创建子线程;
非并发队列执行异步操作,会创建子线程;非并发队列执行同步操作,不会创建主线程。
4,延时操作。规定一定的时间以后去执行某个任务,其中执行的线程可是主线程,也可是子线程,例如:
//用户队列延时操作,属于子线操作。 dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL); double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1 dispatch_after(popTime, userQueue, ^(void){ // 2 NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); //输出 2, dispatch_queue_create - - - <NSThread: 0x6000026efc40>{number = 3, name = (null)} - - - 0 /*****主线程队列延时操作*****/ double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2 NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); //输出 2, dispatch_queue_create - - - <NSThread: 0x600003050d40>{number = 1, name = main} - - - 1 /*****全局队列延时操作*****/ //全局队列操作,任务属于子线操作 dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1 dispatch_after(popTime, gQueue, ^(void){ // 2 NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); });
所以主线程队列是在主进程中执行,全局队列和用户队列,系统会自动创建子线程,然后执行任务。如果当前队列(全局队列和用户队列)中的任务和主线程任务是同步,那么得等其上边的任务执行完之后再去执行其队列的任务,所以定义为主线程操作。
5,文件读取锁操作,当一个文件被多个地方写入的时候,可能会对文件造成损坏或者不完整,那么需要文件需要加锁操作,一般情况都是写入的时候加锁,读取的时候不需要。文件锁需要两个栅栏函数:dispatch_barrier_sync,dispatch_barrier_async。
dispatch_barrier_sync会将队列中的任务进行分割,等待执行dispatch_barrier_sync函数前面的任务执行,然后再执行dispatch_barrier_sync中的任务,其次再执行其下边的任务,且它是在主线程中执行,会阻塞主线程;
dispatch_barrier_async会将队列中的任务进行分割,等待执行dispatch_barrier_sync函数前面的任务执行,然后再执行dispatch_barrier_async中的任务,其次再执行其下边的任务,且它是在子线程中执行,不会阻塞主线程;
例如:
dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(userQueue, ^{ NSLog(@"11111111111%@",[NSThread currentThread].description); }); dispatch_async(userQueue, ^{ NSLog(@"00000000000%@",[NSThread currentThread].description); }); dispatch_barrier_sync(userQueue, ^{ NSLog(@"dispatch_barrier_async - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]); }); dispatch_async(userQueue, ^{ NSLog(@"2222222222%@",[NSThread currentThread].description); }); dispatch_async(userQueue, ^{ NSLog(@"3333333333%@",[NSThread currentThread].description); }); dispatch_async(userQueue, ^{ NSLog(@"4444444444%@",[NSThread currentThread].description); }); //输出: 2020-11-12 16:10:26.471394+0800 xxxxDemo[84233:5342588] 00000000000<NSThread: 0x600000c75680>{number = 5, name = (null)} 2020-11-12 16:10:26.471394+0800 xxxxDemo[84233:5342592] 11111111111<NSThread: 0x600000c17b40>{number = 6, name = (null)} 2020-11-12 16:10:26.471627+0800 xxxxDemo[84233:5342469] dispatch_barrier_async - - - <NSThread: 0x600000c38440>{number = 1, name = main} - - - 1 2020-11-12 16:10:26.471852+0800 xxxxDemo[84233:5342592] 2222222222<NSThread: 0x600000c17b40>{number = 6, name = (null)} 2020-11-12 16:10:26.471853+0800 xxxxDemo[84233:5342588] 3333333333<NSThread: 0x600000c75680>{number = 5, name = (null)} 2020-11-12 16:10:26.471859+0800 xxxxDemo[84233:5342584] 4444444444<NSThread: 0x600000c020c0>{number = 3, name = (null)}
dispatch_barrier_async和dispatch_barrier_sync的区别为:dispatch_barrier_async不阻塞主线程;
dispatch_barrier_sync阻塞主线程,非得等到栅栏里的任务执行完成程序才能执行主线程的任务;
栅栏函数只对主队列和自身所在队列有影响,其他队列不受影响。
网上贴出来一个多读单写的列子:
- (id)readDataForKey:(NSString *)key { __block id result; dispatch_sync(_concurrentQueue, ^{ result = [self valueForKey:key]; }); return result; } - (void)writeData:(id)data forKey:(NSString *)key { dispatch_barrier_async(_concurrentQueue, ^{ [self setValue:data forKey:key]; }); }
6,线程组,dispatch_group_t,用于处理较复杂的一些操作。
具体项目中用到的场景,例如多图片上传,需要上传完之后通知主线程做某种操作,如下代码:
dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); for (NSInteger i = 0; i < 10; i++) { dispatch_group_async(group, userQueue, ^{ //这里可以进行上传图片 sleep(1); NSLog(@"%ld - - - %@ \n",i,[NSThread currentThread].description); }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"刷新页面"); }); /******输出******/ 2020-11-07 16:52:56.789853+0800 git_test[45646:2714650] 0 - - - <NSThread: 0x600003e52680>{number = 3, name = (null)} 2020-11-07 16:52:56.789991+0800 git_test[45646:2714657] 6 - - - <NSThread: 0x600003e5c000>{number = 7, name = (null)} 2020-11-07 16:52:56.790058+0800 git_test[45646:2714658] 7 - - - <NSThread: 0x600003e5c040>{number = 8, name = (null)} 2020-11-07 16:52:56.790158+0800 git_test[45646:2714647] 1 - - - <NSThread: 0x600003e5c080>{number = 9, name = (null)} 2020-11-07 16:52:56.790194+0800 git_test[45646:2714649] 3 - - - <NSThread: 0x600003e5c0c0>{number = 10, name = (null)} 2020-11-07 16:52:56.790223+0800 git_test[45646:2714648] 2 - - - <NSThread: 0x600003e52740>{number = 4, name = (null)} 2020-11-07 16:52:56.791025+0800 git_test[45646:2714656] 5 - - - <NSThread: 0x600003e53040>{number = 6, name = (null)} 2020-11-07 16:52:56.790588+0800 git_test[45646:2714655] 4 - - - <NSThread: 0x600003e53080>{number = 5, name = (null)} 2020-11-07 16:52:56.796564+0800 git_test[45646:2714660] 9 - - - <NSThread: 0x600003e5c380>{number = 11, name = (null)} 2020-11-07 16:52:56.796633+0800 git_test[45646:2714659] 8 - - - <NSThread: 0x600003e52900>{number = 12, name = (null)} 2020-11-07 16:52:56.796839+0800 git_test[45646:2714582] 刷新页面
//但是以上上传有一个问题就是,for循环里边dispatch_group_async的异步操作,不是按照顺序进行,如果类型微博里边的9张图,用户必须按照顺序去拼图,那么会有一些误差,所以修改为: dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, userQueue, ^{ for (NSInteger i = 0; i < 10; i++) { NSLog(@"%ld - - - %@ \n",i,[NSThread currentThread].description); } }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"刷新页面"); }); //这样就可以按照顺序去上传了 /******输出******/ 2020-11-07 16:54:32.754027+0800 git_test[45673:2716334] 0 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} 2020-11-07 16:54:32.754199+0800 git_test[45673:2716334] 1 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} 2020-11-07 16:54:32.754322+0800 git_test[45673:2716334] 2 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} 2020-11-07 16:54:32.754437+0800 git_test[45673:2716334] 3 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} 2020-11-07 16:54:32.754543+0800 git_test[45673:2716334] 4 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} 2020-11-07 16:54:32.754683+0800 git_test[45673:2716334] 5 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)} ...... 2020-11-07 16:54:32.770803+0800 git_test[45673:2716193] 刷新页面
dispatch_group_async于dispatch_group_notify配合使用,dispatch_group_async只有异步方法,没有dispatch_group_sync。 当dispatch_group_async中的执行完会通知dispatch_group_notify去执行任务。
7,希望在应用的生命周期内仅被调度一次的代码块函数,dispatch_once,通常用于单例创建,目的是为了线程安全操作:
+ (id)sharedsharedTestManager { static dispatch_once_t onceQueue; static sharedTestManager *sharedTestManager = nil; dispatch_once(&onceQueue, ^{ sharedTestManager = [[self alloc] init]; }); return sharedTestManager; } //===================== void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
8,信号量,dispatch_semaphore_t。用来控制访问资源的数量的标识,其主要作用是:
1,保持线程同步,将异步执行任务转换为同步执行任务;
2,保证线程安全,为线程加锁。
创建信号量,参数:
1),信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
//提高信号量dispatch_semaphore_signal(信号量)
创建的参数value是指,在当前信号操作下,同事有几个线程并发操作,不能小于0,如下例子:
以上多图片顺序上传亦可用此操作:
dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); for (NSInteger i = 0; i < 10; i++) { dispatch_async(userQueue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //这里可以进行图片上传操作 NSLog(@"begin %ld",i); sleep(1); NSLog(@"end %ld",i); dispatch_semaphore_signal(semaphore); }); } //输出则按照顺序一个一个去执行,如果value为2,则会双双去执行: 2020-11-07 17:25:25.135139+0800 git_test[46129:2742208] begin 0 2020-11-07 17:25:26.135419+0800 git_test[46129:2742208] end 0 2020-11-07 17:25:26.135602+0800 git_test[46129:2742211] begin 2 2020-11-07 17:25:27.140482+0800 git_test[46129:2742211] end 2 2020-11-07 17:25:27.140739+0800 git_test[46129:2742210] begin 1 2020-11-07 17:25:28.141881+0800 git_test[46129:2742210] end 1 2020-11-07 17:25:28.142088+0800 git_test[46129:2742218] begin 3 2020-11-07 17:25:29.145829+0800 git_test[46129:2742218] end 3 2020-11-07 17:25:29.146019+0800 git_test[46129:2742209] begin 4 2020-11-07 17:25:30.147681+0800 git_test[46129:2742209] end 4 2020-11-07 17:25:30.147994+0800 git_test[46129:2742222] begin 5 2020-11-07 17:25:31.152211+0800 git_test[46129:2742222] end 5 2020-11-07 17:25:31.152491+0800 git_test[46129:2742223] begin 6 2020-11-07 17:25:32.156475+0800 git_test[46129:2742223] end 6 2020-11-07 17:25:32.157495+0800 git_test[46129:2742224] begin 7 2020-11-07 17:25:33.159167+0800 git_test[46129:2742224] end 7 2020-11-07 17:25:33.159422+0800 git_test[46129:2742225] begin 8 2020-11-07 17:25:34.162234+0800 git_test[46129:2742225] end 8 2020-11-07 17:25:34.162526+0800 git_test[46129:2742226] begin 9 2020-11-07 17:25:35.166981+0800 git_test[46129:2742226] end 9