iOS多線程——Dispatch Source

使用 Dispatch Source 而不使用 dispatch_async 的唯一原因就是利用聯(lián)結(jié)的優(yōu)勢(shì)坦报。

聯(lián)結(jié)的大致流程:在任一線程上調(diào)用它的一個(gè)函數(shù) dispatch_source_merge_data 后岳遥,會(huì)執(zhí)行 Dispatch Source 事先定義好的句柄(可以把句柄簡(jiǎn)單理解為一個(gè) block )励七。

這個(gè)過(guò)程叫 Custom event 团赁,用戶事件。是 dispatch source 支持處理的一種事件。

簡(jiǎn)單地說(shuō)捌显,這種事件是由你調(diào)用 dispatch_source_merge_data 函數(shù)來(lái)向自己發(fā)出的信號(hào)。

一总寒、創(chuàng)建dispatch源

dispatch_source_t source = dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)

參數(shù):

參數(shù) 意義
type dispatch源可處理的事件
handle 可以理解為句柄扶歪、索引或id,假如要監(jiān)聽進(jìn)程摄闸,需要傳入進(jìn)程的ID
mask 可以理解為描述善镰,提供更詳細(xì)的描述,讓它知道具體要監(jiān)聽什么
queue 自定義源需要的一個(gè)隊(duì)列年枕,用來(lái)處理所有的響應(yīng)句柄(block)

Dispatch Source可處理的所有事件

名稱 內(nèi)容
DISPATCH_SOURCE_TYPE_DATA_ADD 自定義的事件炫欺,變量增加
DISPATCH_SOURCE_TYPE_DATA_OR 自定義的事件,變量OR
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口發(fā)送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_PROC 進(jìn)程監(jiān)聽,如進(jìn)程的退出熏兄、創(chuàng)建一個(gè)或更多的子線程品洛、進(jìn)程收到UNIX信號(hào)
DISPATCH_SOURCE_TYPE_READ IO操作,如對(duì)文件的操作摩桶、socket操作的讀響應(yīng)
DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信號(hào)時(shí)響應(yīng)
DISPATCH_SOURCE_TYPE_TIMER 定時(shí)器
DISPATCH_SOURCE_TYPE_VNODE 文件狀態(tài)監(jiān)聽桥状,文件被刪除、移動(dòng)硝清、重命名
DISPATCH_SOURCE_TYPE_WRITE IO操作辅斟,如對(duì)文件的操作、socket操作的寫響應(yīng)

注意:

  • DISPATCH_SOURCE_TYPE_DATA_ADD
    當(dāng)同一時(shí)間芦拿,一個(gè)事件的的觸發(fā)頻率很高砾肺,那么Dispatch Source會(huì)將這些響應(yīng)以ADD的方式進(jìn)行累積,然后等系統(tǒng)空閑時(shí)最終處理防嗡,如果觸發(fā)頻率比較零散变汪,那么Dispatch Source會(huì)將這些事件分別響應(yīng)。

  • DISPATCH_SOURCE_TYPE_DATA_OR 和上面的一樣蚁趁,是自定義的事件裙盾,但是它是以O(shè)R的方式進(jìn)行累積

二、一些函數(shù)

dispatch_suspend(queue) //掛起隊(duì)列

dispatch_resume(source) //分派源創(chuàng)建時(shí)默認(rèn)處于暫停狀態(tài)他嫡,在分派源分派處理程序之前必須先恢復(fù)

dispatch_source_merge_data //向分派源發(fā)送事件番官,需要注意的是,不可以傳遞0值(事件不會(huì)被觸發(fā))钢属,同樣也不可以傳遞負(fù)數(shù)徘熔。

dispatch_source_set_event_handler //設(shè)置響應(yīng)分派源事件的block,在分派源指定的隊(duì)列上運(yùn)行

dispatch_source_get_data //得到分派源的數(shù)據(jù)

uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源創(chuàng)建淆党,即調(diào)用dispatch_source_create的第二個(gè)參數(shù)

unsigned long dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源創(chuàng)建酷师,即調(diào)用dispatch_source_create的第三個(gè)參數(shù)

void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件處理--即不再調(diào)用block讶凉。如果調(diào)用dispatch_suspend只是暫停dispatch源。

long dispatch_source_testcancel(dispatch_source_t source); //檢測(cè)是否dispatch源被取消山孔,如果返回非0值則表明dispatch源已經(jīng)被取消

void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler); //dispatch源取消時(shí)調(diào)用的block懂讯,一般用于關(guān)閉文件或socket等,釋放相關(guān)資源

void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t registration_handler); //可用于設(shè)置dispatch源啟動(dòng)時(shí)調(diào)用block台颠,調(diào)用完成后即釋放這個(gè)block褐望。也可在dispatch源運(yùn)行當(dāng)中隨時(shí)調(diào)用這個(gè)函數(shù)。
    
  

三串前、代碼

實(shí)例一:dispatch_source的基本用法
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0));
    
    dispatch_source_set_event_handler(source, ^{
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            
            //更新UI
        });
    });
    
    dispatch_resume(source);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        //網(wǎng)絡(luò)請(qǐng)求
        
        dispatch_source_merge_data(source, 1); //通知隊(duì)列
    });
實(shí)例二:dispatch_source實(shí)例

上面的例子創(chuàng)建一個(gè)source瘫里,source的type為ADD的方式,然后將事件觸發(fā)后要執(zhí)行的句柄添加到main隊(duì)列里荡碾,在source創(chuàng)建后默認(rèn)是掛起的减宣,需要用dispatch_resume函數(shù)來(lái)恢復(fù)監(jiān)聽,后面為了測(cè)試監(jiān)聽玩荠,加入了一個(gè)for循環(huán)漆腌,用dispatch_source_merge_data來(lái)觸發(fā)事件,但是在觸發(fā)事件的響應(yīng)句柄里我們只打印了一次阶冈,結(jié)果是每次相加的和闷尿,也就是10,而不是打印了4次女坑。

原因:DISPATCH_SOURCE_TYPE_DATA_ADD是將所有觸發(fā)結(jié)果相加填具,最后統(tǒng)一執(zhí)行響應(yīng),但是加入sleepForTimeInterval后匆骗,如果interval的時(shí)間越長(zhǎng)劳景,則每次觸發(fā)都會(huì)響應(yīng),但是如果interval的時(shí)間很短碉就,則會(huì)將觸發(fā)后的結(jié)果相加后統(tǒng)一觸發(fā)盟广。

這在更新UI時(shí)很有用,比如更新進(jìn)度條時(shí)瓮钥,沒(méi)必要每次觸發(fā)都響應(yīng)筋量,因?yàn)楦聲r(shí)還有其他的用戶操作(用戶輸入,觸碰等)碉熄,所以可以統(tǒng)一觸發(fā)


    //創(chuàng)建source桨武,以DISPATCH_SOURCE_TYPE_DATA_ADD的方式進(jìn)行累加,而DISPATCH_SOURCE_TYPE_DATA_OR是對(duì)結(jié)果進(jìn)行二進(jìn)制或運(yùn)算
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
    //事件觸發(fā)后執(zhí)行的句柄
    dispatch_source_set_event_handler(source,^{
        
        NSLog(@"監(jiān)聽函數(shù):%lu",dispatch_source_get_data(source));
        
    });
    
    //開啟source
    dispatch_resume(source);
    
    dispatch_queue_t myqueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(myqueue, ^ {
        
        for(int i = 1; i <= 4; i ++){
            
            NSLog(@"~~~~~~~~~~~~~~%d", i);
            
            //觸發(fā)事件锈津,向source發(fā)送事件呀酸,這里i不能為0,否則觸發(fā)不了事件
            dispatch_source_merge_data(source,i);
            
            //當(dāng)Interval的事件越長(zhǎng)琼梆,則每次的句柄都會(huì)觸發(fā)
            //[NSThread sleepForTimeInterval:0.0001];
        }
    });
3. 使用timer定時(shí)器
dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)

參數(shù):

  • source 分派源
  • start 數(shù)控制計(jì)時(shí)器第一次觸發(fā)的時(shí)刻性誉。參數(shù)類型是 dispatch_time_t窿吩,這是一個(gè)opaque類型,我們不能直接操作它艾栋。我們得需要 dispatch_time 和 dispatch_walltime 函數(shù)來(lái)創(chuàng)建它們。另外蛉顽,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用蝗砾。
  • interval 間隔時(shí)間
  • leeway 計(jì)時(shí)器觸發(fā)的精準(zhǔn)程度

下面代碼可用作開屏倒計(jì)時(shí)


    //倒計(jì)時(shí)時(shí)間
    __block int timeout = 3;
    
    //創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //創(chuàng)建timer
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    //設(shè)置1s觸發(fā)一次,0s的誤差
    dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒執(zhí)行
    
    //觸發(fā)的事件
    dispatch_source_set_event_handler(_timer, ^{
        
        if(timeout<=0){ //倒計(jì)時(shí)結(jié)束携冤,關(guān)閉
            
            //取消dispatch源
            dispatch_source_cancel(_timer);
            
        }
        else{
            
            timeout--;
            
            dispatch_async(dispatch_get_main_queue(), ^{
                
                //更新主界面的操作
                
                NSLog(@"~~~~~~~~~~~~~~~~%d", timeout);
                
            });
        }
    });
    
    //開始執(zhí)行dispatch源
    dispatch_resume(_timer);
實(shí)例四:dispatch_suspend掛起隊(duì)列
    //創(chuàng)建DISPATCH_QUEUE_SERIAL隊(duì)列
    dispatch_queue_t queue1 = dispatch_queue_create("com.iOSChengXuYuan.queue1", 0);
    dispatch_queue_t queue2 = dispatch_queue_create("com.iOSChengXuYuan.queue2", 0);
    
    //創(chuàng)建group
    dispatch_group_t group = dispatch_group_create();
    
    //異步執(zhí)行任務(wù)
    dispatch_async(queue1, ^{
        NSLog(@"任務(wù) 1 : queue 1...");
        sleep(1);
        NSLog(@":white_check_mark:完成任務(wù) 1");
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù) 1 : queue 2...");
        sleep(1);
        NSLog(@":white_check_mark:完成任務(wù) 2");
    });
    
    //將隊(duì)列加入到group
    dispatch_group_async(group, queue1, ^{
        NSLog(@":no_entry_sign:正在暫停 1");
        dispatch_suspend(queue1);
    });
    
    dispatch_group_async(group, queue2, ^{
        NSLog(@":no_entry_sign:正在暫停 2");
        dispatch_suspend(queue2);
    });
    
    //等待兩個(gè)queue執(zhí)行完畢后再執(zhí)行
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"=======等待兩個(gè)queue完成, 再往下進(jìn)行...");
    
    //異步執(zhí)行任務(wù)
    dispatch_async(queue1, ^{
        NSLog(@"任務(wù) 2 : queue 1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"任務(wù) 2 : queue 2");
    });

    //在這里將這兩個(gè)隊(duì)列重新恢復(fù)
    dispatch_resume(queue1);
    dispatch_resume(queue2);
    
    
    //當(dāng)將dispatch_group_wait(group, DISPATCH_TIME_FOREVER);注釋后悼粮,會(huì)產(chǎn)生崩潰,因?yàn)樗械娜蝿?wù)都是異步執(zhí)行的曾棕,在執(zhí)行恢復(fù)queue1和queue2隊(duì)列的時(shí)候扣猫,可能這個(gè)時(shí)候還沒(méi)有執(zhí)行queue1和queue2的掛起隊(duì)列
實(shí)例五:進(jìn)度條例子
    //1、指定DISPATCH_SOURCE_TYPE_DATA_ADD翘地,做成Dispatch Source(分派源)申尤。設(shè)定Main Dispatch Queue 為追加處理的Dispatch Queue
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
    __block NSUInteger totalComplete = 0;
    
    dispatch_source_set_event_handler(source, ^{
        
        //當(dāng)處理事件被最終執(zhí)行時(shí),計(jì)算后的數(shù)據(jù)可以通過(guò)dispatch_source_get_data來(lái)獲取衙耕。這個(gè)數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會(huì)被重置昧穿,所以totalComplete的值是最終累積的值。
        NSUInteger value = dispatch_source_get_data(source);
        
        totalComplete += value;
        
        NSLog(@"進(jìn)度:%@", @((CGFloat)totalComplete/100));
        
        NSLog(@":large_blue_circle:線程號(hào):%@", [NSThread currentThread]);
    });
    
    //分派源創(chuàng)建時(shí)默認(rèn)處于暫停狀態(tài)橙喘,在分派源分派處理程序之前必須先恢復(fù)时鸵。
    dispatch_resume(source);
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //2、恢復(fù)源后厅瞎,就可以通過(guò)dispatch_source_merge_data向Dispatch Source(分派源)發(fā)送事件:
    for (NSUInteger index = 0; index < 100; index++) {
        
        dispatch_async(queue, ^{
            
            dispatch_source_merge_data(source, 1);
            
            NSLog(@":recycle:線程號(hào):%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
            
            usleep(20000);//0.02秒
            
        });
    }
    
    //3饰潜、比較上面的for循環(huán)代碼,將dispatch_async放在外面for循環(huán)的外面和簸,打印結(jié)果不一樣
    //dispatch_async(queue, ^{
    //
    //    for (NSUInteger index = 0; index < 100; index++) {
    //
    //        dispatch_source_merge_data(source, 1);
    //
    //        NSLog(@":recycle:線程號(hào):%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
    //
    //        usleep(20000);//0.02秒
    //    }
    //});
    
    
    //2是將100個(gè)任務(wù)添加到queue里面彭雾,而3是在queue里面添加一個(gè)任務(wù),而這一個(gè)任務(wù)做了100次循環(huán)

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锁保,一起剝皮案震驚了整個(gè)濱河市冠跷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌身诺,老刑警劉巖蜜托,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異霉赡,居然都是意外死亡橄务,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門穴亏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蜂挪,“玉大人重挑,你說(shuō)我怎么就攤上這事√匿蹋” “怎么了谬哀?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)严肪。 經(jīng)常有香客問(wèn)我史煎,道長(zhǎng),這世上最難降的妖魔是什么驳糯? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任篇梭,我火速辦了婚禮,結(jié)果婚禮上酝枢,老公的妹妹穿的比我還像新娘恬偷。我一直安慰自己,他們只是感情好帘睦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布袍患。 她就那樣靜靜地躺著,像睡著了一般竣付。 火紅的嫁衣襯著肌膚如雪协怒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天卑笨,我揣著相機(jī)與錄音孕暇,去河邊找鬼。 笑死赤兴,一個(gè)胖子當(dāng)著我的面吹牛妖滔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桶良,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼座舍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了陨帆?” 一聲冷哼從身側(cè)響起曲秉,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疲牵,沒(méi)想到半個(gè)月后承二,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纲爸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年亥鸠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡负蚊,死狀恐怖神妹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情家妆,我是刑警寧澤鸵荠,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站伤极,受9級(jí)特大地震影響蛹找,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜塑荒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一熄赡、第九天 我趴在偏房一處隱蔽的房頂上張望姜挺。 院中可真熱鬧齿税,春花似錦、人聲如沸炊豪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)词渤。三九已至牵舱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缺虐,已是汗流浹背芜壁。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留高氮,地道東北人慧妄。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像剪芍,于是被迫代替她去往敵國(guó)和親塞淹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 程序中同步和異步是什么意思罪裹?有什么區(qū)別饱普? 解釋一:異步調(diào)用是通過(guò)使用單獨(dú)的線程執(zhí)行的。原始線程啟動(dòng)異步調(diào)用状共,異步調(diào)...
    風(fēng)繼續(xù)吹0閱讀 1,024評(píng)論 1 2
  • Dispatch Sources 現(xiàn)代系統(tǒng)通常提供異步接口套耕,允許應(yīng)用向系統(tǒng)提交請(qǐng)求,然后在系統(tǒng)處理請(qǐng)求時(shí)應(yīng)用可以繼...
    好雨知時(shí)節(jié)浩宇閱讀 3,810評(píng)論 2 5
  • Dispatch Sources 現(xiàn)代系統(tǒng)通常提供異步接口峡继,允許應(yīng)用向系統(tǒng)提交請(qǐng)求箍铲,然后在系統(tǒng)處理請(qǐng)求時(shí)應(yīng)用可以繼...
    YangPu閱讀 302評(píng)論 0 0
  • 支持原創(chuàng) 現(xiàn)代系統(tǒng)通常提供異步接口,允許應(yīng)用向系統(tǒng)提交請(qǐng)求鬓椭,然后在系統(tǒng)處理請(qǐng)求時(shí)應(yīng)用可以繼續(xù)處理自己的事情颠猴。Gra...
    John_LS閱讀 3,569評(píng)論 3 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理关划,服務(wù)發(fā)現(xiàn),斷路器翘瓮,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139