一步步学习ios

dispatch_semaphore_t

2020-09-01  本文已影响0人  冷武橘

一、信号量

semaphore叫做“信号量”

1、创建信号量
dispatch_semaphore_create(long value); 给信号量初始一个值,当传递的值小于0,信号量将初始化失败返回NULL。

    dispatch_semaphore_t  semaphore1 = dispatch_semaphore_create(0);
    
    dispatch_semaphore_t  semaphore2 = dispatch_semaphore_create(1);
    
    dispatch_semaphore_t  semaphore3 = dispatch_semaphore_create(10);

2、 发送信号量
dispatch_semaphore_signal(dispatch_semaphore_t deem); // 发送信号量后,信号量的值将加+1

dispatch_semaphore_signal(semaphore);
  

3、 等待信号量
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); // 等待信号量,传入的信号量的值将-1。如果传入信号量的值等于0,函数将持续等待不返回

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

iOS线程同步方案性能比较
性能从高到低排序
os_unfair_lock // 缺点:iOS10才支持
OSSpinLock  // 缺点:可能出现优先级反转 已经不再安全 苹果也不推荐使用
dispatch_semaphore // 推荐使用
pthread_mutex  // 优点:跨平台 互斥锁(普通锁) 推荐使用
dispatch_queue(DISPATCH_QUEUE_SERIAL) // c
NSLock   // oc
NSCondition   // oc
pthread_mutex(recursive) // 递归锁
NSRecursiveLock  // oc
NSConditionLock // oc
@synchronized // 递归锁 oc

屏幕快照 2019-05-10 下午3.01.54.png

如以上假如创建了A、B 、C、D四个子线程,假如A线程先执行了34行代码后,此时信号量的值-1也就是1,并往下继续执行。随后B线程也执行了34行代码后,此时信号量的值-1,也就是0。C线程、D线程都执行14行代码时,此时信号量的值已经是0了,C线程和D线程就会进入休眠等待中,此时就卡住34行代码处。假如第11秒时,线程A、B执行完了第38行代码后,信号量+1,+1此时信号量就是2了,这样C线程、D线程会被唤醒继续执行。这样信号量的初始值,可以用来控制线程并发访问的最大数量。同理当我们设置信号量的初时值为1时,就可以实现线程同步。

二、实用案例

2.1、控制线程并发数

//定义一个信号量,初始化为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    
//同时执行100个任务
 for (int i = 0; i < 100; i++)
 {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
    //当前信号量-1
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
    NSLog(@"任务%d执行",i+1);
            
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrlString]];

    dispatch_async(dispatch_get_main_queue(), ^{
        //TODO:刷新界面
    });
            
    //当前信号量+1
    dispatch_semaphore_signal(semaphore);
            
    });
}

2.2 线程安全

通过信号量可以用来控制线程并发访问的最大数量,当我们设置信号量的初时值为1时,就可以实现线程同步。

- (void)viewDidLoad {
    [super viewDidLoad];
    semaphore =  dispatch_semaphore_create(1);
    self.array = [NSMutableArray array];
    for (int i=0; i<100; i++) {
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil];
        [thread start];
    }
}

- (void)test{
    NSLog(@"测试开始");
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    sleep(3);
    [self.array addObject:@"0"];
    dispatch_semaphore_signal(semaphore);
    NSLog(@"测试");
}

self.array直接在多个线程上进行做修改操作是不安全的,我可以通过信息量同步加锁保证其线程安全

2.3、多个网络请求同步

#pragma mark - 网络请求一
- (void)getuserId:(dispatch_semaphore_t)semaphore{
   AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
   sessionmanger.responseSerializer=[AFHTTPResponseSerializer serializer];
   [sessionmanger POST:@"https://www.baidu.com/" parameters:nil constructingBodyWithBlock:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"请求成功1%@", [NSThread currentThread]);
        useid=@"1234";
       dispatch_semaphore_signal(semaphore);
   } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       NSLog(@"%@",error);
        dispatch_semaphore_signal(semaphore);
   }];
   
}

#pragma mark - 网络请求二
- (void)requestwithuserid:(NSString *)userid{
   NSDictionary *parms=[NSMutableDictionary dictionary];
   [parms setValue:userid forKey:@"userid"];
   AFHTTPSessionManager *sessionmanger=[[AFHTTPSessionManager alloc]init];
   sessionmanger.responseSerializer=[AFHTTPResponseSerializer serializer];
   [sessionmanger POST:@"https://www.baidu.com/" parameters:userid constructingBodyWithBlock:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       NSLog(@"请求成功2");
   } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
       NSLog(@"%@",error);
   }];
}

#pragma mark - 使用信号量实现
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_semaphore_t semaphore= dispatch_semaphore_create(0); // 创建信号量
    [self getuserId:semaphore];//获取用户useid
 
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//当前信号量为0,一直等待阻塞线程
    [self requestwithuserid:useid];
}

command+R运行一下,没有任何反应。
原因分析:线程卡住了。代码执行到dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER)因为信号量为0,当前线程会被阻塞。而当前线程是主线程,网络请求一成功后回调到主线程,因为主线程被阻塞 造成信号量无法释放,一直卡住。
解决方案就是开启一个异步的线程

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建一个并行队列
    dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queque, ^{
        dispatch_semaphore_t semaphore= dispatch_semaphore_create(0); // 创建信号量
        [self getuserId:semaphore];
        dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
        [self requestwithuserid:useid];
    });
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    dispatch_group_t group = dispatch_group_create();
    // 创建信号量
   semaphore = dispatch_semaphore_create(0);
    // 创建全局并行
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_async(group, queue, ^{

        [self request1];

    });
    
    dispatch_group_async(group, queue, ^{
       
        [self request2];
 
    });
    
    dispatch_group_notify(group, queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        //在这里 进行请求后的方法,回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"更新UI");
            
        });
    });
    NSLog(@"12344");
    
}
- (void)request1{
    AFHTTPSessionManager *manger=[[AFHTTPSessionManager alloc]init];
    manger.responseSerializer=[AFHTTPResponseSerializer serializer];
    [manger POST:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"请求成功1");
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         dispatch_semaphore_signal(semaphore);
    }];

}
- (void)request2{
    AFHTTPSessionManager *manger=[[AFHTTPSessionManager alloc]init];
    manger.responseSerializer=[AFHTTPResponseSerializer serializer];
    [manger POST:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"请求成功2");
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         dispatch_semaphore_signal(semaphore);
    }];
   
}

或者这样

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    dispatch_group_t group = dispatch_group_create();
   
    // 创建全局并行
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_async(group, queue, ^{

        [self request1];

    });
    
    dispatch_group_async(group, queue, ^{
       
        [self request2];
 
    });
    
    dispatch_group_notify(group, queue, ^{

        //在这里 进行请求后的方法,回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"更新UI");
            
        });
    });
    NSLog(@"12344");
    
}
- (void)request1{
    // 创建信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    AFHTTPSessionManager *manger=[[AFHTTPSessionManager alloc]init];
    manger.responseSerializer=[AFHTTPResponseSerializer serializer];
    [manger POST:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"请求成功1");
         NSLog(@"%ld", dispatch_semaphore_signal(semaphore));
       ;
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         dispatch_semaphore_signal(semaphore);
        NSLog(@"%@",semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
- (void)request2{
    // 创建信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    AFHTTPSessionManager *manger=[[AFHTTPSessionManager alloc]init];
    manger.responseSerializer=[AFHTTPResponseSerializer serializer];
    [manger POST:@"https://www.baidu.com" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"请求成功2");
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         dispatch_semaphore_signal(semaphore);
    }];
     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
@end
上一篇下一篇

猜你喜欢

热点阅读