首先,我们明确一下同步和异步概念
同步和异步强调的是消息通信机制 (synchronous communication/ asynchronous communication)。

  • 同步,就是在发出一个"调用"时,在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由“调用者”主动等待这个“调用”的结果。
  • 异步是"调用"在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在"调用"发出后,"被调用者"通过状态、通知来通知调用者,或通过回调函数处理这个调用

异步转为同步,例如在开发中有时需要等网络请求完成之后拿到数据做一些操作,而且有时是同时好几个网络请求同时发起。这时会有对异步操作进行更进一步控制的场景,不单网络请求,有时一些其他本地文件,多张图片处理等可能都会遇到这种操作,GCD中就有很多这方面处理的api。

  1. 利用并发队列和栅栏函数对异步操作进行控制。
// 创建队列
dispatch_queue_t queue = dispatch_queue_create("task", DISPATCH_QUEUE_CONCURRENT);
// 添加任务
dispatch_async(queue, ^{
    NSLog(@"1===task===%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"2===task===%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"3===task===%@", [NSThread currentThread]);
});

// 与dispatch_barrier_async区别就是它的block里代码是否在主线程执行
dispatch_barrier_sync(queue, ^{
    NSLog(@"===barrier==%@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"4===task===%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"5===task===%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"6===task===%@", [NSThread currentThread]);
});

上述代码打印如下:

异步消息推送 异步消息传递_UIresponder

从打印可以看出 dispatch_barrier_sync 栅栏函数后 task 4,5,6 在 task1,2,3 执行完后才执行的。做到了多个线程与另外多个线程的前后同步

  1. 使用调度组进行分发操作 dispatch_group_t ,代码如下:
//下面代码等两个网络请求结束后会打印任务完成。
   dispatch_group_t group = dispatch_group_create();
     
     dispatch_group_enter(group);
     dispatch_group_enter(group);
     // A网络请求
     [NetWorkManerger  URL:kBaseUrl sBlock:^(id result) {
         NSLog(@"%@", result);
         dispatch_group_leave(group);
     } fBlcok:^(NSError *error) {
        NSLog(@"%@", error);
         dispatch_group_leave(group);
     }];
     
     // B网络请求
     [NetWorkManerger  URL:kBaseUrl  sBlock:^(id result) {
         NSLog(@"%@", result);
         dispatch_group_leave(group);
     } fBlcok:^(NSError *error) {
         NSLog(@"%@", error);
         dispatch_group_leave(group);
     }];
   
     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务完成==%@", [NSThread currentThread]);
     });

请求前调用 dispatch_group_enter(),请求结束后调用 dispatch_group_leave() ,只有当所有的 enter 都 leave后,dispatch_group_notify() 的block才会执行。

注意:  dispatch_group_enter 与 dispatch_group_leave 一定要成对出现。

  1. 使用信号量 dispatch_semaphore_t 对并发进行控制

信号量这里可以看作是资源标识,只有当它信号数大于0才可以往后面执行,它有三个对应的 api 。

dispatch_semaphore_create //创建一个信号,并指定初始的信号数
dispatch_semaphore_signal //使对应的信号数加1
dispatch_semaphore_wait //使对应的信号数量减1,如果执行到这行代码时信号数量已经为0,那么在指定时间后才会去执行它后面的代码,指定时间为它的第二个参数,如果设置为  DISPATCH_TIME_FOREVER 将一直等待。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

dispatch_queue_t queue = dispatch_queue_create("task", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"1===task===%@", [NSThread currentThread]);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
        dispatch_semaphore_signal(semaphore);
    });
});

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
    NSLog(@"2===task===%@", [NSThread currentThread]);
});

上述代码打印结果如下

异步消息推送 异步消息传递_UIresponder_02


上面信号量代码中一开始创建 semaphore 信号数就是0,所以 dispatch_semaphore_wait 后面的代码要等到信号数不为0才会去执行,在 task1 执行完毕后用 dispatch_semaphore_signal 给信号数加1,所以 task2 在task1执行完毕之后执行,做到了同步。

延伸:线程之间消息传递

1、子线程与主线程消息传递
(1)通过block实现

异步函数对外调用者(主线程)提供block,调用者(主线程)调用异步函数只需要将需要异步函数结果的操作放到block内即可,异步函数内部通过dispatch_async将线程切换到主线程

//调用
  [self blockCallbackInMain:^(NSString *result) {
      NSLog(@"我在%@,收到了%@的消息",[NSThread currentThread],result);
  }];
//函数
-(void)blockCallbackInMain:(void(^)(NSString *result))block
{
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"我在%@",[NSThread currentThread]);
        for (int i = 0; i<=1000; i++)
        {
        }
       dispatch_async(dispatch_get_main_queue(), ^{
            if (block)
            {
                block([NSString stringWithFormat:@"%@",[NSThread currentThread]]);
            }
        });
    });
}
我在<NSThread: 0x2830ff480>{number = 5, name = (null)}
我在<_NSMainThread: 0x2830b8880>{number = 1, name = main},收到了<_NSMainThread: 0x2830b8880>{number = 1, name = main}的消息
(2)通过信号量实现
-(void)semaphoreSync
{
//    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    
    __block int j = 0;
    __block NSString *str = @"";
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        str = [NSString stringWithFormat:@"%@",[NSThread currentThread]];
        for (int i = 0; i<=3000; i++)
        {
            if (i==3000)
            {
                j=3;
                
            }
        } 
//        dispatch_semaphore_signal(sema);
    });
//    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
    NSLog(@"我%@从异步线程出来,j的值是%d",str,j);
    if (j!=0)
    {
        NSLog(@"我是%@,从%@异步线程获取内容啦,j的值不等于0",[NSThread currentThread],str);
    }
}

我从异步线程出来,j的值是0
我是<_NSMainThread: 0x281798880>{number = 1, name = main},从<NSThread: 0x2817d5940>{number = 3, name = (null)}异步线程获取内容啦,j的值不等于0

如果将代码中注释的部分释放出来,也就是使用信号量来实现子线程到主线程的同步,输出结果如下

我<NSThread: 0x2837980c0>{number = 6, name = (null)}从异步线程出来,j的值是3
我是<_NSMainThread: 0x2837d4880>{number = 1, name = main},从<NSThread: 0x2837980c0>{number = 6, name = (null)}异步线程获取内容啦,j的值不等于0
2、子线程与子线程线程消息传递

也就是在A子线程中等待B线程的执行结果,通过信号量实现的话也是上面的方式,只不过在A子线程中调用即可

(1)调用
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"我是调用方,我在%@",[NSThread currentThread]);
        [self semaphoreSync];
        sleep(10);
    });
 (2)函数
 -(void)semaphoreSync
{
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    
    __block int j = 0;
    __block NSString *str = @"";
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        str = [NSString stringWithFormat:@"%@",[NSThread currentThread]];
        for (int i = 0; i<=3000; i++)
        {
            if (i==3000)
            {
                j=3;
                
            }
        }
       
        dispatch_semaphore_signal(sema);
    });
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
    if (j!=0)
    {
        NSLog(@"我是%@,从%@异步线程获取内容啦,j的值不等于0",[NSThread currentThread],str);
    }
}

我是调用方,我在<NSThread: 0x2828bfa40>{number = 3, name = (null)}
我是函数,我在<NSThread: 0x2828ee700>{number = 7, name = (null)}
我是<NSThread: 0x2828bfa40>{number = 3, name = (null)},从<NSThread: 0x2828ee700>{number = 7, name = (null)}异步线程获取内容啦,j的值不等于0

那么我们会想,在子线程中调用block,会怎么样呢

(1)调用
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        NSLog(@"我是调用方,我在%@",[NSThread currentThread]);
        [self blockCallback:^(NSString *result) {
            NSLog(@"我在%@,收到了%@的消息",[NSThread currentThread],result);
        }];
        sleep(10);
    });
    
    
  (2)异步函数
-(void)blockCallback:(void (^)(NSString *result))block
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"我是函数,我在%@",[NSThread currentThread]);
        for (int i = 0; i<=5000; i++)
        {
            if(i == 4000 )
            {
                if (block)
                {
                    block([NSString stringWithFormat:@"%@",[NSThread currentThread]]);
                }
            }
        }
    });
}

我是调用方,我在<NSThread: 0x283fd86c0>{number = 4, name = (null)}
我是函数,我在<NSThread: 0x283fdc400>{number = 5, name = (null)}
我在<NSThread: 0x283fdc400>{number = 5, name = (null)},收到了<NSThread: 0x283fdc400>{number = 5, name = (null)}的消息

结论:

1、blcok同步是哪个线程执行blcok,block就在哪个线程,也会在该线程同步信息

2、信号量,是调用,同步信息给哪个线程

通过上面可以看出异步同步到主线程是异步同步到另一个子线程的特例