dispatch_semaphore使用

dispatch_semaphore是GCD采用線程同步的一種方式,與他相關(guān)的共有三個(gè)參數(shù):

dispatch_semaphore_create
dispatch_semaphore_signal
dispatch_semaphore_wait

  • dispatch_semaphore_create 創(chuàng)建信號(hào)量
    dispatch_semaphore_create(long value); 給信號(hào)量初始一個(gè)值噩死,當(dāng)傳遞的值小于0焙压,信號(hào)量將初始化失敗返回NULL怕篷。
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(2);
  • dispatch_semaphore_signal 發(fā)送信號(hào)量
    long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
    這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值加1;
    dispatch_semaphore_signal(semaphore);
  • dispatch_semaphore_wait 等待信號(hào)量
    long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
    這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema的值減1揍障,如果傳入信號(hào)量的值等于0脉课,函數(shù)將持續(xù)等待不返回救军。
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

這個(gè)函數(shù)的作用是這樣的财异,如果dsema信號(hào)量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句唱遭,并且將信號(hào)量的值減1戳寸;
  如果desema的值為0,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout(注意timeout的類型為dispatch_time_t拷泽,
  不能直接傳入整形或float型數(shù))疫鹊,如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了,
  且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號(hào)量司致,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1拆吆。
  如果等待期間沒有獲取到信號(hào)量或者信號(hào)量的值一直為0,那么等到timeout時(shí)脂矫,其所處線程自動(dòng)執(zhí)行其后語句枣耀。

  • 信號(hào)量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量庭再。
  • 信號(hào)量的初時(shí)值為1捞奕,代表同時(shí)只允許1條線程訪問資源,保證線程同步拄轻。

iOS線程同步方案性能比較
性能從高到低排序
os_unfair_lock // 缺點(diǎn):iOS10才支持
OSSpinLock  // 缺點(diǎn):可能出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn) 已經(jīng)不再安全 蘋果也不推薦使用
dispatch_semaphore // 推薦使用
pthread_mutex  // 優(yōu)點(diǎn):跨平臺(tái) 互斥鎖(普通鎖) 推薦使用
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的性能是很好的極力推薦使用。

dispatch_semaphore.jpg

如以上假如創(chuàng)建了A哺眯、B 谷浅、C、D四個(gè)子線程奶卓,假如A線程先執(zhí)行了35行代碼后一疯,此時(shí)信號(hào)量的值-1也就是1,并往下繼續(xù)執(zhí)行夺姑。隨后B線程也執(zhí)行了35行代碼后墩邀,此時(shí)信號(hào)量的值-1,也就是0盏浙。C線程眉睹、D線程都執(zhí)行35行代碼時(shí),此時(shí)信號(hào)量的值已經(jīng)是0了废膘,C線程和D線程就會(huì)進(jìn)入休眠等待中竹海,此時(shí)就卡住35行代碼處。假如第11秒時(shí)丐黄,線程A斋配、B執(zhí)行完了第39行代碼后,信號(hào)量+1,+1此時(shí)信號(hào)量就是2了,這樣C線程艰争、D線程會(huì)被喚醒繼續(xù)執(zhí)行坏瞄。這樣信號(hào)量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量甩卓。同理當(dāng)我們設(shè)置信號(hào)量的初時(shí)值為1時(shí)鸠匀,就可以實(shí)現(xiàn)線程同步。

dispatch_semaphore 使用案例

控制線程并發(fā)數(shù)
//定義一個(gè)信號(hào)量逾柿,初始化為10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
    
//同時(shí)執(zhí)行100個(gè)任務(wù)
 for (int i = 0; i < 100; i++)
 {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
    //當(dāng)前信號(hào)量-1
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            
    NSLog(@"任務(wù)%d執(zhí)行",i+1);
            
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrlString]];

    dispatch_async(dispatch_get_main_queue(), ^{
        //TODO:刷新界面
    });
            
    //當(dāng)前信號(hào)量+1
    dispatch_semaphore_signal(semaphore);
            
    });
}
線程安全

通過信號(hào)量可以用來控制線程并發(fā)訪問的最大數(shù)量缀棍,當(dāng)我們設(shè)置信號(hào)量的初時(shí)值為1時(shí),就可以實(shí)現(xiàn)線程同步机错。

- (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直接在多個(gè)線程上進(jìn)行做修改操作是不安全的睦柴,我可以通過信息量同步加鎖保證其線程安全。

多個(gè)網(wǎng)絡(luò)請求同步
  • 1毡熏、首先通過網(wǎng)絡(luò)請求一獲取用戶useid,之后用userid為參數(shù)發(fā)起網(wǎng)絡(luò)請求二侣诵。
#pragma mark - 網(wǎng)絡(luò)請求一
- (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 - 網(wǎng)絡(luò)請求二
- (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 - 使用信號(hào)量實(shí)現(xiàn)
- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_semaphore_t semaphore= dispatch_semaphore_create(0); // 創(chuàng)建信號(hào)量
    [self getuserId:semaphore];//獲取用戶useid
 
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);//當(dāng)前信號(hào)量為0痢法,一直等待阻塞線程
    [self requestwithuserid:useid];
}

command+R運(yùn)行一下,沒有任何反應(yīng)杜顺。
原因分析:線程卡住了财搁。代碼執(zhí)行到dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER)因?yàn)樾盘?hào)量為0,當(dāng)前線程會(huì)被阻塞躬络。而當(dāng)前線程是主線程尖奔,網(wǎng)絡(luò)請求一成功后回調(diào)到主線程,因?yàn)橹骶€程被阻塞 造成信號(hào)量無法釋放穷当,一直卡住提茁。

解決方案就是開啟一個(gè)異步線程

- (void)viewDidLoad {
    [super viewDidLoad];
    //創(chuàng)建一個(gè)并行隊(duì)列
    dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queque, ^{
        dispatch_semaphore_t semaphore= dispatch_semaphore_create(0); // 創(chuàng)建信號(hào)量
        [self getuserId:semaphore];
        dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
        [self requestwithuserid:useid];
    });
}
  • 2、多個(gè)網(wǎng)絡(luò)請求后刷新UI
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    dispatch_group_t group = dispatch_group_create();
    // 創(chuàng)建信號(hào)量
   semaphore = dispatch_semaphore_create(0);
    // 創(chuàng)建全局并行
    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);
        //在這里 進(jìn)行請求后的方法馁菜,回到主線程
        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();
   
    // 創(chuàng)建全局并行
    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, ^{

        //在這里 進(jìn)行請求后的方法茴扁,回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            
            NSLog(@"更新UI");
            
        });
    });
    NSLog(@"12344");
    
}
- (void)request1{
    // 創(chuàng)建信號(hào)量
    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{
    // 創(chuàng)建信號(hào)量
    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

參考:CodeVicent

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汪疮,隨后出現(xiàn)的幾起案子峭火,更是在濱河造成了極大的恐慌,老刑警劉巖智嚷,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卖丸,死亡現(xiàn)場離奇詭異,居然都是意外死亡盏道,警方通過查閱死者的電腦和手機(jī)稍浆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粹湃,你說我怎么就攤上這事恐仑。” “怎么了为鳄?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵裳仆,是天一觀的道長。 經(jīng)常有香客問我孤钦,道長歧斟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任偏形,我火速辦了婚禮静袖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俊扭。我一直安慰自己队橙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布萨惑。 她就那樣靜靜地躺著捐康,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庸蔼。 梳的紋絲不亂的頭發(fā)上解总,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音姐仅,去河邊找鬼花枫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掏膏,可吹牛的內(nèi)容都是我干的劳翰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼壤追,長吁一口氣:“原來是場噩夢啊……” “哼磕道!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起行冰,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤溺蕉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后悼做,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疯特,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年肛走,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漓雅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖邻吞,靈堂內(nèi)的尸體忽然破棺而出组题,到底是詐尸還是另有隱情,我是刑警寧澤抱冷,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布崔列,位于F島的核電站,受9級(jí)特大地震影響旺遮,放射性物質(zhì)發(fā)生泄漏赵讯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一耿眉、第九天 我趴在偏房一處隱蔽的房頂上張望边翼。 院中可真熱鬧,春花似錦鸣剪、人聲如沸组底。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斤寇。三九已至,卻和暖如春拥褂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牙寞。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工饺鹃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人间雀。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓悔详,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惹挟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茄螃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355