iOS多线程-GCD+代码示例

562 阅读15分钟

GCD

主队列

在主线程中调用同步执行+主队列

//在主线程中调用同步执行+主队列
-(void)syncMain{
    NSLog(@"currentHead---%@",[NSThread currentThread]);
    NSLog(@"syncMain begin");
    // 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        //追加任务1
        [NSThread sleepForTimeInterval:2];  //模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        //追加任务2
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    NSLog(@"syncMain end");
}
打印结果如下

**2021-03-26 16:47:18.322216+0800 GCD-test[5676:241439] currentHead---<NSThread: 0x600001930080>{number = 1, name = main}**

**2021-03-26 16:47:18.322446+0800 GCD-test[5676:241439] syncMain begin**

**(lldb)** 

追加到主队列中的任务1、2都没有执行,且最后的syncMain end也没有打印,程序直接崩溃,这是为什么?

因为我们在主线程中执行 syncMain 方法,相当于把syncMain 任务放到了主线程的队列中

而同步执行会等待当前队列中的任务执行完毕,才会接着执行

当我们又把任务1追加到主队列中时,也就是将任务1放到主线程中执行

此时,主线程的队列正在执行syncMain 任务,即任务1在等待syncMain 任务的执行完毕,而syncMain 任务又需要等待任务1执行完毕,才能接着执行下面的任务2等

最终导致了**syncMain 任务和任务1都在等待对方执行完毕**,导致了死锁

在其他线程中调用同步执行+主队列

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//    [self syncMain];
    [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
}


//在其他中调用同步执行+主队列
-(void)syncMain{
    NSLog(@"currentHead---%@",[NSThread currentThread]);
    NSLog(@"syncMain begin");
    // 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        //追加任务1
        [NSThread sleepForTimeInterval:2];  //模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        //追加任务2
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    NSLog(@"syncMain end");
}
打印结果如下
2021-03-26 16:57:24.693109+0800 GCD-test[5793:248991] currentHead---<NSThread: 0x600003c86f00>{number = 5, name = (null)}
2021-03-26 16:57:24.693560+0800 GCD-test[5793:248991] syncMain begin
2021-03-26 16:57:26.695160+0800 GCD-test[5793:248916] 1---<NSThread: 0x600003cc8240>{number = 1, name = main}
2021-03-26 16:57:28.696918+0800 GCD-test[5793:248916] 2---<NSThread: 0x600003cc8240>{number = 1, name = main}
2021-03-26 16:57:28.697189+0800 GCD-test[5793:248991] syncMain end

可以看到syncMain 任务在主线程中正常执行,任务顺序执行

因为syncMain 任务放在了其他线程中,而任务1、2被追加到了主队列中,在主线程中执行,而主队列此时没有正在执行的任务,所以主线程会执行任务1,任务1执行完毕后,再继续将任务2添加到主队列中,此时主队列也没有其他任务,主线程也不会被卡住,不会造成死锁

异步执行+主队列

-(void)asyncMain{
    NSLog(@"currentHead---%@",[NSThread currentThread]);
    NSLog(@"asyncMain begin");
    // 获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        //追加任务1
        [NSThread sleepForTimeInterval:2];  //模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        //追加任务2
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    NSLog(@"asyncMain end");
}
打印结果如下
2021-03-26 17:04:17.984120+0800 GCD-test[5917:254579] currentHead---<NSThread: 0x600000dc0380>{number = 1, name = main}
2021-03-26 17:04:17.984304+0800 GCD-test[5917:254579] asyncMain begin
2021-03-26 17:04:17.984478+0800 GCD-test[5917:254579] asyncMain end
2021-03-26 17:04:19.985017+0800 GCD-test[5917:254579] 1---<NSThread: 0x600000dc0380>{number = 1, name = main}
2021-03-26 17:04:21.985997+0800 GCD-test[5917:254579] 2---<NSThread: 0x600000dc0380>{number = 1, name = main}

所有任务都是在主线程中执行的,没有开启其他线程,异步执行虽然具备开启新线程的能力,但因为是主队列,所以所有任务都放在主线程中

任务是在打印完 asyncMain beginasyncMain end后执行的,是因为异步执行不会做任何等待,可以继续执行任务

GCD线程间的通信

在iOS开发中,我们一般在主线程中刷新UI,例如:点击、滚动、拖拽等事件

我们通常把一些耗时操作放在其他线程,例如:文件上传、图片下载等耗时操作

而有时当我们在其他线程执行完耗时操作后,需要回到主线程,那么就用到了线程间的通信

//线程间通信
-(void)communicate{
    //获得全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        //异步追加任务1
        [NSThread sleepForTimeInterval:2];  //模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);
        
        //回到主线程
        dispatch_async(mainQueue, ^{
            //追加在主线程中执行的任务
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2---%@",[NSThread currentThread]);
        });
    });
}
打印结果
2021-03-26 17:17:51.144443+0800 GCD-test[6057:264059] 1---<NSThread: 0x600000702b40>{number = 5, name = (null)}
2021-03-26 17:17:53.144938+0800 GCD-test[6057:263886] 2---<NSThread: 0x600000740400>{number = 1, name = main}

可以看到先在其他线程执行完任务1,然后回到主线程执行任务2

GCD的其他方法

栅栏函数:dispatch_barrier_async

异步执行+并发队列时会开启新的线程,且任务并发执行,但是我们无法设置任务执行的顺序

//异步执行+并发队列
-(void)asycnConcurrent{
    NSLog(@"currentHead---%@",[NSThread currentThread]);
    NSLog(@"asycnConcurrent begin");
    // 创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("hahaha", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        //追加任务1
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        //追加任务2
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    
    dispatch_async(queue, ^{
        //追加任务3
        NSLog(@"3---%@",[NSThread currentThread]);
    });
    
    NSLog(@"asycnConcurrent end");
}
打印结果:可以看到任务执行顺序是2、3、1
2021-03-26 17:36:24.123041+0800 GCD-test[6331:277548] currentHead---<NSThread: 0x6000029980c0>{number = 1, name = main}
2021-03-26 17:36:24.123234+0800 GCD-test[6331:277548] asycnConcurrent begin
2021-03-26 17:36:24.123398+0800 GCD-test[6331:277548] asycnConcurrent end
2021-03-26 17:36:24.123460+0800 GCD-test[6331:277635] 2---<NSThread: 0x6000029cec40>{number = 6, name = (null)}
2021-03-26 17:36:24.123578+0800 GCD-test[6331:277633] 3---<NSThread: 0x6000029d39c0>{number = 7, name = (null)}
2021-03-26 17:36:24.123583+0800 GCD-test[6331:277636] 1---<NSThread: 0x6000029de700>{number = 3, name = (null)}

为了控制并发队列的任务执行顺序,例如有任务1、2、3,我们要求任务3必须要在任务1、2完成后再执行,应该怎么办?

栅栏函数可以控制多线程异步的任务执行顺序

注意,栅栏函数不能使用全局并发队列

//栅栏函数
-(void)barrier{
    dispatch_queue_t queue = dispatch_queue_create("hahaha", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        //追加任务1
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        //追加任务2
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        //追加任务barrier
        NSLog(@"------barrier------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        //追加任务3
        NSLog(@"3---%@",[NSThread currentThread]);
    });
}
打印结果
2021-03-26 17:44:31.413739+0800 GCD-test[6463:285495] 1---<NSThread: 0x6000019e80c0>{number = 6, name = (null)}
2021-03-26 17:44:31.413746+0800 GCD-test[6463:285498] 2---<NSThread: 0x6000019f57c0>{number = 7, name = (null)}
2021-03-26 17:44:33.415331+0800 GCD-test[6463:285359] ------barrier------<NSThread: 0x6000019a8240>{number = 1, name = main}
2021-03-26 17:44:35.420804+0800 GCD-test[6463:285498] 3---<NSThread: 0x6000019f57c0>{number = 7, name = (null)}

可以看到通过在中间设置栅栏后,任务3是在任务1、2都执行完后才执行的,但是在栅栏函数前的任务1、2的执行顺序还是无法控制的


延迟执行:dispatch_after

GCD的延迟执行方法

//延迟执行-GCD
-(void)delay_gcd{
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"GCD delay --- %@",[NSThread currentThread]);
    });
}
打印结果
2021-03-26 22:30:59.741605+0800 GCD-test[9721:429049] GCD delay --- <NSThread: 0x6000019f8280>{number = 1, name = main}

使用GCD的延迟执行的好处是可以选择想要执行的任务在哪个线程中完成,上面这个例子是在主线程中执行的,下面来看一个在子线程中执行的例子

//延迟执行-GCD
-(void)delay_gcd{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"GCD delay --- %@",[NSThread currentThread]);
    });
}
打印结果
2021-03-26 22:35:48.193273+0800 GCD-test[9804:433026] GCD delay --- <NSThread: 0x600001718bc0>{number = 4, name = (null)}

除了GCD之外还有两种常用的延迟执行方法

  1. //延迟执行——1
    -(void)delay_performselector{
        [self performSelector:@selector(task) withObject:nil afterDelay:2];
    }
    
  2. //延迟执行-2
    -(void)delay_nstimer{
        [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(task) userInfo:nil repeats:NO];
    }
    

一次性代码:dispatch_once

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self once];
}

-(void)once{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"dispatch_once --- %@",[NSThread currentThread]);
    });
}
打印结果
注意:即使再次点击屏幕也不会再打印
2021-03-26 22:40:31.463259+0800 GCD-test[9911:438448] dispatch_once --- <NSThread: 0x600000238400>{number = 1, name = main}

一次性代码在整个应用程序的生命周期只会执行一次,可以看到onceToken是用static修饰的,是全局的,且只会在应用程序释放的时候这个变量才会被释放掉

一次性代码的常用场景:单例模式

(所以注意!一次性代码不能放在懒加载里面)

快速迭代

//第一个参数:遍历的次数

//第二个参数:队列 (注意只能传并发队列)

//第三个参数:index 索引
-(void)apply_demo{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zd---%@",index,[NSThread currentThread]);
    });
  
}
打印结果
2021-03-26 23:13:58.750090+0800 GCD-test[10433:468003] 1---<NSThread: 0x600002e7e940>{number = 6, name = (null)}
2021-03-26 23:13:58.750102+0800 GCD-test[10433:468000] 0---<NSThread: 0x600002e110c0>{number = 5, name = (null)}
2021-03-26 23:13:58.750102+0800 GCD-test[10433:468002] 2---<NSThread: 0x600002e72e80>{number = 4, name = (null)}
2021-03-26 23:13:58.750316+0800 GCD-test[10433:468000] 5---<NSThread: 0x600002e110c0>{number = 5, name = (null)}
2021-03-26 23:13:58.750316+0800 GCD-test[10433:468003] 4---<NSThread: 0x600002e7e940>{number = 6, name = (null)}
2021-03-26 23:13:58.750330+0800 GCD-test[10433:468002] 6---<NSThread: 0x600002e72e80>{number = 4, name = (null)}
2021-03-26 23:13:58.750525+0800 GCD-test[10433:468000] 8---<NSThread: 0x600002e110c0>{number = 5, name = (null)}
2021-03-26 23:13:58.750525+0800 GCD-test[10433:468003] 9---<NSThread: 0x600002e7e940>{number = 6, name = (null)}
2021-03-26 23:13:58.750525+0800 GCD-test[10433:468002] 7---<NSThread: 0x600002e72e80>{number = 4, name = (null)}
2021-03-26 23:13:58.750145+0800 GCD-test[10433:467851] 3---<NSThread: 0x600002e38740>{number = 1, name = main}
2021-03-26 23:13:58.752066+0800 GCD-test[10433:467851] apply---end

可以看到打印不一定是按照0-9的顺序的,因为是并发执行的

但是apply---end一定是在最后执行,因为 dispatch_apply方法会等待全部任务执行完毕

快速迭代顾名思义就是快速的迭代,相对于for循环可以更快的执行

for循环是在主线程中执行且是同步执行的,而快速迭代内部会开子线程,然后有子线程和主线程一起来并发的执行任务

快速迭代的简单应用,举个例子:文件的转移

-(void)moveFileWithGCD{
    //1. 拿到文件路径
    NSString *from = @"/Users/juice/Desktop/from";
    //2. 拿到目标文件路径
    NSString *to = @"/Users/juice/Desktop/to";
    //3. 得到目录下面的所有文件名
    NSArray *subPaths = [[NSFileManager defaultManager] subpathsAtPath:from];
    //4. 遍历所有文件,执行剪切操作
    NSInteger count = subPaths.count;
    dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        //4.1 拼接文件的全路径,这个方法会自动加上“/”
        NSString *fullPath = [from stringByAppendingPathComponent:subPaths[index]];
        NSString *toFullPath = [to stringByAppendingPathComponent:subPaths[index]];
        //4.2 执行剪切操作
        //第一个参数:要被剪切的文件路径
        //第二个参数:文件要被存放到哪个位置
        [[NSFileManager defaultManager]moveItemAtPath:fullPath toPath:toFullPath error:nil];
        NSLog(@"%@----%@----%@",fullPath,toFullPath,[NSThread currentThread]);
    });
}
打印结果
2021-03-26 23:35:50.944679+0800 GCD-test[10770:484969] /Users/juice/Desktop/from/.DS_Store----/Users/juice/Desktop/to/.DS_Store----<NSThread: 0x60000177cb00>{number = 1, name = main}
2021-03-26 23:35:50.944725+0800 GCD-test[10770:485221] /Users/juice/Desktop/from/截屏2021-03-26 23.27.10.png----/Users/juice/Desktop/to/截屏2021-03-26 23.27.10.png----<NSThread: 0x600001723940>{number = 6, name = (null)}
2021-03-26 23:35:50.944824+0800 GCD-test[10770:485127] /Users/juice/Desktop/from/截屏2021-03-26 23.27.17.png----/Users/juice/Desktop/to/截屏2021-03-26 23.27.17.png----<NSThread: 0x60000172a980>{number = 7, name = (null)}
2021-03-26 23:35:50.944839+0800 GCD-test[10770:485220] /Users/juice/Desktop/from/截屏2021-03-26 23.27.15.png----/Users/juice/Desktop/to/截屏2021-03-26 23.27.15.png----<NSThread: 0x6000017205c0>{number = 8, name = (null)}

队列组

例如当一个队列组执行完毕后才执行下一个队列组,即监听一个队列组任务的完成状态

dispatch_group_notify

-(void)groupNotify{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"3---%@",[NSThread currentThread]);
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"4---%@",[NSThread currentThread]);
        NSLog(@"group---end");
    });
}
2021-03-26 23:54:27.197455+0800 GCD-test[11021:498748] 1---<NSThread: 0x600003a07980>{number = 5, name = (null)}
2021-03-26 23:54:27.197490+0800 GCD-test[11021:498750] 2---<NSThread: 0x600003a7cb00>{number = 6, name = (null)}
2021-03-26 23:54:27.197521+0800 GCD-test[11021:498747] 3---<NSThread: 0x600003a64300>{number = 7, name = (null)}
2021-03-26 23:54:27.197659+0800 GCD-test[11021:498747] 4---<NSThread: 0x600003a64300>{number = 7, name = (null)}
2021-03-26 23:54:27.197755+0800 GCD-test[11021:498747] group---end

当group所有的任务执行完毕后,notify通知(拦截通知)

注意notify不是阻塞的,这个方法本身也是异步的,如果notify下面还有方法会继续执行

dispatch_group_enter、dispatch_group_leave

-(void)groupEnterAndLeave{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        //追加任务1
         NSLog(@"1---%@",[NSThread currentThread]);
        //离开群组
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);

    dispatch_async(queue, ^{
        //追加任务2
         NSLog(@"2---%@",[NSThread currentThread]);
        //离开群组
        dispatch_group_leave(group);
    });
}

dispatch_group_enter和dispatch_group_leave必须要配对使用

dispatch_async(queue, ^{
   //追加任务2
   NSLog(@"2---%@",[NSThread currentThread]);
   //离开群组
   dispatch_group_leave(group);
});

//其实就等价于

dispatch_group_async(group, queue, ^{
        NSLog(@"2---%@",[NSThread currentThread]);
});

dispatch_group_wait

-(void)groupWait{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"3---%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"4---%@",[NSThread currentThread]);
    });
    
    NSLog(@"-----end----");
}
打印结果
2021-03-27 15:39:05.993676+0800 GCD-test[17321:752515] 1---<NSThread: 0x600000b8bd80>{number = 5, name = (null)}
2021-03-27 15:39:05.993672+0800 GCD-test[17321:752516] 2---<NSThread: 0x600000b8a640>{number = 4, name = (null)}
2021-03-27 15:39:05.993986+0800 GCD-test[17321:752469] -----end----
2021-03-27 15:39:05.994110+0800 GCD-test[17321:752515] 4---<NSThread: 0x600000b8bd80>{number = 5, name = (null)}
2021-03-27 15:39:05.994426+0800 GCD-test[17321:752516] 3---<NSThread: 0x600000b8a640>{number = 4, name = (null)}

dispatch_group_wait是死等,阻塞的,直到队列组的所有任务都执行完毕后才能执行

应用场景

  1. 下载图片1,开子线程
  2. 下载图片2,开子线程
  3. 合成图片并显示图片,开子线程

可以看到步骤3依赖于步骤1、2的完成才可执行

-(void)download{
    //创建队列组和队列
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //开启子线程
    dispatch_async(queue, ^{
        NSLog(@"image1----%@",[NSThread currentThread]);
        //1.下载图片1
        //1.1 确定URL
        NSURL *url = [NSURL URLWithString:@"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2894455039,838130676&fm=26&gp=0.jpg"];
        //1.2 下载二进制数据
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        //1.3 转换图片
        self.image1 = [UIImage imageWithData:imageData];
    });

    //开启子线程
    dispatch_async(queue, ^{
        NSLog(@"image2----%@",[NSThread currentThread]);
        //2.下载图片2
        //2.1 确定URL
        NSURL *url = [NSURL URLWithString:@"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201906%2F23%2F20190623142123_SZva4.thumb.400_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1619515368&t=e8c1d14fc46795e6e739a7061ad384b8"];
        //2.2 下载二进制数据
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        //2.3 转换图片
        self.image2 = [UIImage imageWithData:imageData];
    });

    //3. 合并图片
    dispatch_group_notify(group, queue, ^{
        NSLog(@"合并image----%@",[NSThread currentThread]);
        //3.1 创建图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(400, 400));
        //3.2 画图1
        [self.image1 drawInRect:CGRectMake(0, 0, 400, 200)];
        //3.3 画图2
        [self.image2 drawInRect:CGRectMake(0, 200, 400, 200)];
        //3.4 根据上下文得到一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        //3.5 关闭上下文
        UIGraphicsEndImageContext();
        //3.6 更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI----%@",[NSThread currentThread]);
            self.imageView.image = image;
        });
    });
}
打印结果
2021-03-28 17:35:34.227509+0800 GCD-test[1313:33317] image1----<NSThread: 0x6000033bca40>{number = 6, name = (null)}
2021-03-28 17:35:34.227519+0800 GCD-test[1313:33311] image2----<NSThread: 0x6000033b2140>{number = 7, name = (null)}
2021-03-28 17:35:34.227577+0800 GCD-test[1313:33312] 合并image----<NSThread: 0x6000033aa900>{number = 4, name = (null)}
2021-03-28 17:35:34.243472+0800 GCD-test[1313:33197] 更新UI----<NSThread: 0x6000033ec0c0>{number = 1, name = main}

Simulator Screen Shot - iPhone 11 - 2021-03-28 at 17.36.42.png

自己创建的并发队列和全局并发队列的区别

  1. 全局并发队列在整个应用程序中本身是默认存在的,并且对应有优先级,我们只是选择其中一个优先级的队列来用,而自己创建的是从头开始去创建一个队列

  2. 如果是在MRC环境下,手动创建的并发队列需要我们release(使用create和retain的函数都是如此),在ARC中手动创建的就不需要我们释放了,而全局并发队列不管是ARC还是MRC都不需要我们负责释放

  3. 使用栅栏函数时,只能在手动创建的并发队列一起使用时才有效

补充点

  1. dispatch_async 和 dispatch_async_f 的区别:封装任务的方法不同
  • dispatch_async 是用block
  • dispatch_async_f 是用函数
//第一个参数:队列
//第二个参数:参数
//第三个参数:函数
dispatch_async_f(<#dispatch_queue_t  _Nonnull queue#>, <#void * _Nullable context#>, <#dispatch_function_t  _Nonnull work#>)

点击参数 #dispatch_function_t进去看到函数定义如下:

typedef void (*dispatch_function_t)(void *_Nullable);

我们直接复制后面的 void (*dispatch_function_t)(void *_Nullable);来定义我们的函数

  • (*dispatch_function_t)用函数名(例如task)替换
  • (void *_Nullable)用参数名替换(param)

替换后函数 :void task(void *param)

这里对应的param就是 dispatch_async_f第三个参数传入的值

示例如下

-(void)other{
    dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
    dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
    dispatch_async_f(dispatch_get_global_queue(0, 0), NULL, task);
}

void task(void *param){
    NSLog(@"%s---%@",__func__,[NSThread currentThread]);
}
打印结果
2021-03-28 17:06:07.255900+0800 GCD-test[3410:118187] task---<NSThread: 0x600001fabf00>{number = 7, name = (null)}
2021-03-28 17:06:07.256018+0800 GCD-test[3410:118182] task---<NSThread: 0x600001fcd240>{number = 4, name = (null)}
2021-03-28 17:06:07.256044+0800 GCD-test[3410:118183] task---<NSThread: 0x600001faaf40>{number = 6, name = (null)}
  1. 创建队列的时候第一个参数是C语言的字符串,所以不用在前面加@

参考博客:行走少年郎GCD