GCD信号量使用

作者: coderhlt | 来源:发表于2019-05-10 16:04 被阅读7次

    一、信号量

    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);
    
    • 信号量的初始值,可以用来控制线程并发访问的最大数量
    • 信号量的初时值为1,代表同时只允许1条线程访问资源,保证线程同步。

    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

    • 从上可以知道线程同步除了os_unfair_lock 和
      OSSpinLock之外,dispatch_semaphore的性能是很好的极力推荐使用。
    屏幕快照 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时,就可以实现线程同步。

    二、信号量的使用

    • 1、使用场景一:首先通过网络请求一获取用户useid,之后用userid为参数发起网络请求二。
    #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];
        });
    }
    
    • 2、使用场景二:从客户端有顺序的一张张上传图片
        dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queque, ^{
            for (NSInteger i=0;i<imageArray.count;i++) {
                dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
                [[AFHttpRequest shareRequest]  unifiedPicConversionWithchannelNo:@"" businessNum:@"" serialNum:serialNum sendType:@"" picName:picName picDirection:@"" picIoStr:picBase64Str serverSuccessFn:^(id dict) {
                    dispatch_semaphore_signal(semaphore);
                } serverFailureFn:^(NSError *error) {
                    dispatch_semaphore_signal(semaphore);
          
                }];
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            }
        });
    
    • 3、使用场景三:控制线程并发数
    //定义一个信号量,初始化为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);
                
        });
    }
    

    开始初始化一个信号量,默认为10,然后同时执行100个任务,所以每次最大并发是10。dispatch_semaphore_wait表示是当前的信号量减去1,因为已经启动一个任务,所以通过这个方法来修改信号量的值。同样,当当前任务执行完后,要释放信号量,使当前信号量加1,所以用dispatch_semaphore_signal方法

    • 4、使用场景四:多个网络请求后刷新UI
    - (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
    

    相关文章

      网友评论

        本文标题:GCD信号量使用

        本文链接:https://www.haomeiwen.com/subject/dmzioqtx.html