GCD认识与使用

  • iOS中多线程我用的比较多的是GCD,简单好用,对它的认识也只是处于表面上,理解的并不是很深。最近面试被问了一个“线程同步”的问题,我一下子不知道如何回答。我脑海里有的是串行并行,异步同步。想要A线程执行完任务后再执行B线程,创建一个串行队列,然后同步执行,这样线程不就是按顺序来执行了么?(事实上这样做只有一个线程,串行执行任务而已)面试官告诉我用线程组跟信号量实现,当时我就不淡定了。信号量是啥玩意儿?线程组我倒是用过,的确能够实现GCD线程同步。回来后百度了一下,自己还是太年轻,了解的知识太少。打开Xcode,写个demo复习一下,顺便学学这个“线程同步"。

  • GCD执行任务的代码放在block中,执行任务有两种方式:同步执行和异步执行。两者的主要区别是:是否具备开辟新线程的能力。

    1. 同步执行(sync):只能在当前线程中执行任务,不具备开辟新线程的能力。
    2. 异步执行(async):可以在新的线程中执行任务,开辟了新线程。
  • GCD存放任务的队列有两种:串行队列和并行队列。 串行队列:任务一个接一个地执行。任务A执行完后再执行任务B。 并行队列:多个任务同时执行。

    首先,我们来看看队列是如何创建的。

//串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    
    //并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

1.并行同步

  • 不会开启新线程,串行执行任务
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
 dispatch_sync(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行同步 1-----%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行同步 2-----%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行同步 3-----%@",[NSThread currentThread]);
        }
    });

//输出结果

2017-08-03 15:26:08.978 GCD同步[36401:3070527] 并行同步 1-----<NSThread: 0x60000007bdc0>{number = 1, name = main}
2017-08-03 15:26:08.979 GCD同步[36401:3070527] 并行同步 1-----<NSThread: 0x60000007bdc0>{number = 1, name = main}
2017-08-03 15:26:08.979 GCD同步[36401:3070527] 并行同步 2-----<NSThread: 0x60000007bdc0>{number = 1, name = main}
2017-08-03 15:26:08.980 GCD同步[36401:3070527] 并行同步 2-----<NSThread: 0x60000007bdc0>{number = 1, name = main}
2017-08-03 15:26:08.980 GCD同步[36401:3070527] 并行同步 3-----<NSThread: 0x60000007bdc0>{number = 1, name = main}
2017-08-03 15:26:08.980 GCD同步[36401:3070527] 并行同步 3-----<NSThread: 0x60000007bdc0>{number = 1, name = main}

2.并行异步

  • 开启新线程,并行执行任务
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
 NSLog(@"并行异步开始");
    dispatch_async(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行异步 1-----%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行异步 2-----%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0;i<2;i++){
            NSLog(@"并行异步 3-----%@",[NSThread currentThread]);
        }
    });
    NSLog(@"并行异步结束");

//

2017-08-03 15:45:39.668 GCD同步[36584:3100879] 并行异步开始
2017-08-03 15:45:39.669 GCD同步[36584:3100879] 并行异步结束
2017-08-03 15:45:39.669 GCD同步[36584:3100925] 并行异步 1-----<NSThread: 0x60000026c940>{number = 3, name = (null)}
2017-08-03 15:45:39.669 GCD同步[36584:3100913] 并行异步 3-----<NSThread: 0x60000026ca80>{number = 5, name = (null)}
2017-08-03 15:45:39.669 GCD同步[36584:3100912] 并行异步 2-----<NSThread: 0x60800026d3c0>{number = 4, name = (null)}
2017-08-03 15:45:39.670 GCD同步[36584:3100925] 并行异步 1-----<NSThread: 0x60000026c940>{number = 3, name = (null)}
2017-08-03 15:45:39.670 GCD同步[36584:3100913] 并行异步 3-----<NSThread: 0x60000026ca80>{number = 5, name = (null)}
2017-08-03 15:45:39.671 GCD同步[36584:3100912] 并行异步 2-----<NSThread: 0x60800026d3c0>{number = 4, name = (null)}

3.串行同步

  • 没有开启新线程,串行执行任务
 dispatch_queue_t serialQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
   
        NSLog(@"线程一:%@",[NSThread currentThread]);
  
    });
    dispatch_sync(serialQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"线程二:%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"线程三:%@",[NSThread currentThread]);
    });

//

2017-08-03 15:51:29.832 GCD同步[36626:3109112] 线程一:<NSThread: 0x608000261840>{number = 1, name = main}
2017-08-03 15:51:31.901 GCD同步[36626:3109112] 线程二:<NSThread: 0x608000261840>{number = 1, name = main}
2017-08-03 15:51:32.943 GCD同步[36626:3109112] 线程三:<NSThread: 0x608000261840>{number = 1, name = main}

4.串行异步

  • 有创建新的线程(一个),串行执行任务
NSLog(@"串行异步开始");
    dispatch_queue_t serialQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
        NSLog(@"线程A:%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"线程B:%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"线程C:%@",[NSThread currentThread]);
    });
    
    NSLog(@"串行异步结束");

//

2017-08-03 15:54:42.986 GCD同步[36682:3115254] 串行异步开始
2017-08-03 15:54:42.986 GCD同步[36682:3115254] 串行异步结束
2017-08-03 15:54:42.987 GCD同步[36682:3115288] 线程A:<NSThread: 0x6080000751c0>{number = 3, name = (null)}
2017-08-03 15:54:45.043 GCD同步[36682:3115288] 线程B:<NSThread: 0x6080000751c0>{number = 3, name = (null)}
2017-08-03 15:54:46.102 GCD同步[36682:3115288] 线程C:<NSThread: 0x6080000751c0>{number = 3, name = (null)}

5.主队列同步:dispatch_sync方法不能在主队列中调用,这会无限期的阻止线程并导致死锁。所有通过GCD提交到主队列的任务必须是异步的。但是你可以在其它异步线程中使用主队列+同步。 6.主队列异步:

 NSLog(@"begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"主线程 异步任务一:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"主线程 异步任务二:%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"主线程 异步任务三:%@",[NSThread currentThread]);
    });
    NSLog(@"end");

//

2017-08-03 16:17:18.189 GCD同步[36906:3152036] begin
2017-08-03 16:17:18.189 GCD同步[36906:3152036] end
2017-08-03 16:17:19.274 GCD同步[36906:3152036] 主线程 异步任务一:<NSThread: 0x60800007f640>{number = 1, name = main}
2017-08-03 16:17:20.333 GCD同步[36906:3152036] 主线程 异步任务二:<NSThread: 0x60800007f640>{number = 1, name = main}
2017-08-03 16:17:21.390 GCD同步[36906:3152036] 主线程 异步任务三:<NSThread: 0x60800007f640>{number = 1, name = main}

可以看到所有任务都在主线程中,因为是主队列,并且任务一个接一个执行。

7.GCD的延时执行方法:dispatch_after.一次性代码:dispatch_onece。

8.线程组:当你需要知道任务什么时候结束的时候,可以使用这个。想要任务按顺序执行,就把队列设为串行就可以。如果你在任务中又开辟了子线程,比如说使用AFNetworking进行网络请求,block中返回数据,其实这个地方是返回主线程了,这个时候可以使用线程组和信号量共同实现线程同步。

 NSLog(@"begin");
    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(@"第一%@",[NSThread currentThread]);
  
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"第二%@",[NSThread currentThread]);

    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"第三%@",[NSThread currentThread]);
   
        NSLog(@"第三结束");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"over");
    });
    NSLog(@"end");

//

2017-08-07 16:27:11.260 GCD同步[44450:4263974] begin
2017-08-07 16:27:11.260 GCD同步[44450:4263974] end
2017-08-07 16:27:11.260 GCD同步[44450:4264076] 第一<NSThread: 0x608000078380>{number = 3, name = (null)}
2017-08-07 16:27:11.260 GCD同步[44450:4264079] 第二<NSThread: 0x600000077f00>{number = 4, name = (null)}
2017-08-07 16:27:11.260 GCD同步[44450:4264077] 第三<NSThread: 0x600000078cc0>{number = 5, name = (null)}
2017-08-07 16:27:11.261 GCD同步[44450:4264077] 第三结束
2017-08-07 16:27:11.266 GCD同步[44450:4263974] over

另外一种写法:

    dispatch_queue_t queue = dispatch_get_global_queue( 0, 0 );
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    dispatch_async( queue, ^{
        
        NSLog( @"task 1 --- %@", [NSThread currentThread] );
        dispatch_group_leave(group);
    } );
    dispatch_group_enter(group);
    dispatch_async( queue, ^{
       
        NSLog( @"task 2 --- %@", [NSThread currentThread] );
        dispatch_group_leave(group);
    } );
    dispatch_group_notify( group, queue, ^{
        NSLog( @"all task done %@", [NSThread currentThread] );
    } );

//

2017-08-07 16:28:10.484 GCD同步[44486:4266075] task 1 --- <NSThread: 0x60800006ef00>{number = 3, name = (null)}
2017-08-07 16:28:10.484 GCD同步[44486:4266074] task 2 --- <NSThread: 0x600000075c40>{number = 4, name = (null)}
2017-08-07 16:28:10.485 GCD同步[44486:4266074] all task done <NSThread: 0x600000075c40>{number = 4, name = (null)}

线程组和信号量的共同使用:

 // 创建组
     dispatch_group_t group = dispatch_group_create();
   // 将第一个网络请求任务添加到组中
   dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         // 创建信号量
       dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
       // 开始网络请求任务
       AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
       [manager GET:urlString_1
           parameters:dictionary
             progress:nil
              success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"成功请求数据1:%@",[responseObject class]);
                  // 如果请求成功,发送信号量
                  dispatch_semaphore_signal(semaphore);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"失败请求数据");
                  // 如果请求失败,也发送信号量
                  dispatch_semaphore_signal(semaphore);
              }];
         // 在网络请求任务成功之前,信号量等待中
         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
     });
     // 将第二个网络请求任务添加到组中
     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         // 创建信号量
         dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
         // 开始网络请求任务
         AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
         [manager GET:urlString_2
           parameters:dictionary
             progress:nil
            success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"成功请求数据2:%@",[responseObject class]);
                  // 如果请求成功,发送信号量
                  dispatch_semaphore_signal(semaphore);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"失败请求数据");
                  // 如果请求失败,也发送信号量
                  dispatch_semaphore_signal(semaphore);
              }];
         // 在网络请求任务成功之前,信号量等待中
         dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
     });
     dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         NSLog(@"完成了网络请求,不管网络请求失败了还是成功了。");
    });

转载于:https://my.oschina.net/NycoWang/blog/1501422

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值