iOS多線(xiàn)程——GCD

在iOS中,實(shí)現(xiàn)多線(xiàn)程的方式有很多,比如GCD,NSOperation,NSThread等等,但是一直對(duì)線(xiàn)程的概念模糊,今天就根據(jù)代碼例子來(lái)了解iOS中GCD的用法和原理。

GCD是和block緊密相連的,所以最好先了解下block。
GCD是C level的函數(shù),這意味著它也提供了C的函數(shù)指針作為參數(shù),方便了C程序員。

下面首先來(lái)看GCD的使用:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
async表明異步運(yùn)行,block代表的是你要做的事情,queue則是你把任務(wù)交給誰(shuí)來(lái)處理了。(除了async,還有sync,delay,本文以async為例)。

dispatch隊(duì)列不支持cancel(取消),沒(méi)有實(shí)現(xiàn)dispatch_cancel()函數(shù),不像NSOperationQueue,不得不說(shuō)這是個(gè)小小的缺憾。

之所以程序中會(huì)用到多線(xiàn)程是因?yàn)槌绦蛲鶗?huì)需要讀取數(shù)據(jù),然后更新UI。為了良好的用戶(hù)體驗(yàn),讀取數(shù)據(jù)的操作會(huì)傾向于在后臺(tái)運(yùn)行,這樣以避免阻塞主線(xiàn)程。GCD里就有三種queue來(lái)處理。

GCD Queue分為三種:

  • The main queue:主隊(duì)列,主線(xiàn)程就是在個(gè)隊(duì)列中,系統(tǒng)默認(rèn)就有一個(gè)串行隊(duì)列
  • Global queues: 全局并發(fā)隊(duì)列
  • 用戶(hù)隊(duì)列:是用函數(shù)dispatch_queue_create創(chuàng)建的自定義隊(duì)列

而用戶(hù)隊(duì)列又分為下面兩種:
(1)Serial Dispatch Queue
線(xiàn)程池只提供一個(gè)線(xiàn)程用來(lái)執(zhí)行任務(wù),所以后一個(gè)任務(wù)必須等到前一個(gè)任務(wù)執(zhí)行結(jié)束才能開(kāi)始。(DISPATCH_QUEUE_SERIAL)

(2)Concurrent Dispatch Queue
線(xiàn)程池提供多個(gè)線(xiàn)程來(lái)執(zhí)行任務(wù),所以可以按序啟動(dòng)多個(gè)任務(wù)并發(fā)執(zhí)行。(DISPATCH_QUEUE_CONCURRENT)

關(guān)于同步和異步

  • dispatch_sync
    則調(diào)用用 dispatch_sync的線(xiàn)程會(huì)等dispatch_sync的對(duì)內(nèi)容執(zhí)行完再繼續(xù)執(zhí)行。dispatch_sync函數(shù)不會(huì)立即返回,即阻塞當(dāng)前線(xiàn)程,等待block同步執(zhí)行完成。

  • dispatch_async
    調(diào)用dispatch_async的線(xiàn)程不會(huì)的等dispatch_async的內(nèi)容,自己繼續(xù)執(zhí)行。dispatch_async函數(shù)會(huì)立即返回, block會(huì)在后臺(tái)異步執(zhí)行。

關(guān)于并發(fā)和并行

  • 并行:是多個(gè)任務(wù)在同一個(gè)時(shí)間片段內(nèi)同時(shí)進(jìn)行,比如說(shuō)你一邊吃飯一邊打電話(huà)一遍看電視

  • 并發(fā):是多個(gè)任務(wù)在同一個(gè)時(shí)間的點(diǎn)上可以切換進(jìn)行,比如說(shuō)你先吃著飯,然后電話(huà)來(lái)了,停下吃飯立馬打電話(huà),打完后立馬切換到看電視,這個(gè)你在同一個(gè)線(xiàn)條內(nèi)只有一個(gè)任務(wù)在執(zhí)行

圖示關(guān)于并發(fā)和并行

并行
并發(fā)

下面就以代碼實(shí)例來(lái)分析GCD用法

實(shí)例一:DISPATCH_QUEUE_SERIAL串行隊(duì)列

/**
 DISPATCH_QUEUE_SERIAL是每次運(yùn)行一個(gè)任務(wù),可以添加多個(gè),執(zhí)行次序FIFO。
 **/
- (void)test1
{
    NSDate *date = [NSDate date];
    
    NSString *daStr = [date description];
    
    const char *queueName = [daStr UTF8String];
    
    //DISPATCH_QUEUE_SERIAL等同于NULL
    dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
    
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addTime) userInfo:nil repeats:YES];
    
    //在main_queue中異步將任務(wù)放到myQueue隊(duì)列里
    //放入myQueue的順序是按代碼的執(zhí)行順序:任務(wù)一,任務(wù)二,任務(wù)三(因?yàn)閙ain_queue是順序執(zhí)行)
    //在myQueue取出的順序是按照FIFO的順序
    //因?yàn)檫@是一個(gè)串行的隊(duì)列,所以取出后的任務(wù)是一個(gè)接著一個(gè)執(zhí)行的
    
    
    //執(zhí)行結(jié)果
    //[NSThread sleepForTimeInterval:6]~~~~~~~~6
    //[NSThread sleepForTimeInterval:3]~~~~~~~~9
    //[NSThread sleepForTimeInterval:12]~~~~~~~~21

    
    //任務(wù)一:
    dispatch_async(myQueue, ^{
        
        [NSThread sleepForTimeInterval:6];
        
        NSLog(@"[NSThread sleepForTimeInterval:6]~~~~~~~~%d", t);
        
    });
    
    //任務(wù)二:
    dispatch_async(myQueue, ^{
        
        [NSThread sleepForTimeInterval:3];
        
        NSLog(@"[NSThread sleepForTimeInterval:3]~~~~~~~~%d", t);
        
    });
    
    //任務(wù)三:
    dispatch_async(myQueue, ^{
        
        [NSThread sleepForTimeInterval:12];
        
        NSLog(@"[NSThread sleepForTimeInterval:12]~~~~~~~~%d", t);
        
    });
}

-(void)addTime {
    
    ++t;
}

實(shí)例二:全局的并發(fā)隊(duì)列dispatch_get_global_queue

/**
 可以同時(shí)運(yùn)行多個(gè)任務(wù),每個(gè)任務(wù)的啟動(dòng)時(shí)間是按照加入queue的順序,結(jié)束的順序依賴(lài)各自的任務(wù).
 **/
- (void)test2
{
    dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
    //在main_queue中異步將任務(wù)放到myQueue
    //放入myQueue的順序是:任務(wù)一,任務(wù)二,任務(wù)三
    //在myQueue中取出的順序是FIFO的原則:任務(wù)一,任務(wù)二,任務(wù)三
    //取出的任務(wù)放到線(xiàn)程里執(zhí)行,因?yàn)檫@是一個(gè)并行的隊(duì)列,所以任務(wù)可以同時(shí)運(yùn)行
    
    //執(zhí)行的結(jié)果
    //執(zhí)行的結(jié)果可以是 1 2 3 的隨機(jī)組合
    
    //任務(wù)一:
    dispatch_async(myQueue, ^{
        
        NSLog(@"~~~~~~~~~1");
        
    });
    
    //任務(wù)二:
    dispatch_async(myQueue, ^{
        
        NSLog(@"~~~~~~~~~2");
        
    });
    
    //任務(wù)三:
    dispatch_async(myQueue, ^{
        
        NSLog(@"~~~~~~~~~3");
        
    });
}

實(shí)例三:DISPATCH_QUEUE_SERIAL串行隊(duì)列

/**
 串行隊(duì)列的異步任務(wù):使用一個(gè)子線(xiàn)程依次執(zhí)行。
  對(duì)比一下dispatch_async和dispatch_sync輸出的i的順序和線(xiàn)程的地址
 **/
- (void)test3
{
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    
    for (int i = 0; i < 3; i++) {
        
        //在main_queue中將i=0,1,2的任務(wù)異步加入queue隊(duì)列中
        //加入queue的順序是:i=0, i=1, i=2
        //所以在queue中取出的順序是:i=0,i=1,i=2
        //因?yàn)閝ueue是同步隊(duì)列,所以三個(gè)任務(wù)放到同一個(gè)線(xiàn)程中依次執(zhí)行
        
        //運(yùn)行結(jié)果
        //~~~i=0~~~~<NSThread: 0x600000076940>{number = 3, name = (null)}
        //<NSThread: 0x600000068d00>{number = 1, name = (null)}
        //~~~i=1~~~~<NSThread: 0x600000076940>{number = 3, name = (null)}
        //<NSThread: 0x600000068d00>{number = 1, name = (null)}
        // ~~~i=2~~~~<NSThread: 0x600000076940>{number = 3, name = (null)}
        //<NSThread: 0x600000068d00>{number = 1, name = (null)}

        //當(dāng)是使用DISPATCH_QUEUE_SERIAL和dispatch_sync同步的時(shí)候,三個(gè)任務(wù)執(zhí)行的線(xiàn)程就是當(dāng)前的mainThread中執(zhí)行的

        dispatch_async(queue, ^{
            
            NSLog(@"~~~i=%d~~~~%@", i, [NSThread currentThread]);
            
            NSLog(@"%@",[NSThread mainThread]);
            
        });
    }
}

實(shí)例四:DISPATCH_QUEUE_CONCURRENT并發(fā)隊(duì)列

/**
 并行隊(duì)列的異步任務(wù):使用多個(gè)子線(xiàn)程無(wú)序執(zhí)行,一般任務(wù)較少時(shí)幾個(gè)任務(wù)就開(kāi)幾個(gè)線(xiàn)程,較多時(shí)則開(kāi)部分線(xiàn)程。
 應(yīng)用:一系列的異步任務(wù)沒(méi)有先后順序,結(jié)束無(wú)序
 對(duì)比一下dispatch_async和dispatch_sync輸出的線(xiàn)程的地址
 **/
- (void)test4
{
    
    dispatch_queue_t queue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    
    //在main_queue中異步的將i=0,i=1,i=2的任務(wù)加入到queue中
    //加入queue的順序是i=0,i=1,i=2
    //在queue中取出的順序是i=0,i=1,i=2
    //因?yàn)閝ueue是并發(fā)隊(duì)列,所以有多條的線(xiàn)程來(lái)執(zhí)行三個(gè)任務(wù),thread的執(zhí)行的順序不定
    
    //當(dāng)使用dispatch_sync時(shí),執(zhí)行的順序又成了i=0,i=1,i=2
    
    for (int i = 0; i < 3; i++) {
        
        dispatch_async(queue, ^{
            
            NSLog(@"~~~i=%d~~~~%@", i, [NSThread currentThread]);
            
            NSLog(@"%@",[NSThread mainThread]);
            
        });
    }
}

實(shí)例五:dispatch_group_t組隊(duì)列

/**
 dispatch_group_async可以實(shí)現(xiàn)監(jiān)聽(tīng)一組任務(wù)是否完成,完成后得到通知執(zhí)行其他的操作。
 dispatch_group_async會(huì)監(jiān)聽(tīng)最終的任務(wù)完成后,并通知一個(gè)線(xiàn)程
 這個(gè)方法很有用,比如你執(zhí)行三個(gè)下載任務(wù),當(dāng)三個(gè)任務(wù)都下載完成后你才通知界面說(shuō)完成了。
注意這里不是監(jiān)聽(tīng)queue里所有的任務(wù)完成,而是添加到組里的任務(wù),這個(gè)任務(wù)是在這個(gè)queue里,同時(shí)也在這個(gè)組里,組里所有的任務(wù)的完成并不代表queue里所有的任務(wù)的完成。
 下面是一段例子代碼:
 注意:當(dāng)queue是global(或者DISPATCH_QUEUE_CONCURRENT)隊(duì)列和DISPATCH_QUEUE_SERIAL隊(duì)列時(shí)線(xiàn)程的區(qū)別
 **/
- (void)test5
{
    
    //dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_queue_t queue = dispatch_queue_create("queue3", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"task1~~~~currentThread=%@~~~~~~mainThread%@", [NSThread currentThread], [NSThread mainThread]);
        
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"task2~~~~currentThread=%@~~~~~~mainThread%@", [NSThread currentThread], [NSThread mainThread]);
        
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"task3~~~~currentThread=%@~~~~~~mainThread%@", [NSThread currentThread], [NSThread mainThread]);
        
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        NSLog(@"回到主線(xiàn)程");
        
    });
}

實(shí)例六:dispatch_apply重復(fù)

/**
 dispatch_apply:執(zhí)行某個(gè)代碼片段N次
 
 重復(fù)執(zhí)行block,需要注意的是這個(gè)方法是同步返回,也就是說(shuō)等到所有block執(zhí)行完畢才返回,如需異步返回則嵌套在dispatch_async中來(lái)使用。
 多個(gè)block的運(yùn)行是否并發(fā)或串行執(zhí)行也依賴(lài)queue的是否并發(fā)或串行。
 
 dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
 運(yùn)行結(jié)果:
 array[0]~~~~~~~~~~~~~~0
 array[3]~~~~~~~~~~~~~~3
 array[2]~~~~~~~~~~~~~~2
 array[1]~~~~~~~~~~~~~~1
 array[4]~~~~~~~~~~~~~~4
 array[5]~~~~~~~~~~~~~~5
 
 dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
 運(yùn)行結(jié)果:
 array[0]~~~~~~~~~~~~~~0
 array[1]~~~~~~~~~~~~~~1
 array[2]~~~~~~~~~~~~~~2
 array[3]~~~~~~~~~~~~~~3
 array[4]~~~~~~~~~~~~~~4
 array[5]~~~~~~~~~~~~~~5
 **/
- (void)test6
{
    
    NSArray *array = @[@"0",@"1",@"2",@"3",@"4",@"5"];
    
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
    
    //打印done是在dispatch_apply里所有的任務(wù)執(zhí)行完畢之后才會(huì)執(zhí)行
    dispatch_async(queue2, ^(){
        
        //放在dispatch_apply里面的任務(wù)的執(zhí)行順序完全依賴(lài)于queue1的隊(duì)列串發(fā)還是并發(fā)
        dispatch_apply([array count], queue1, ^(size_t index) {
            
            NSLog(@"array[%ld]~~~~~~~~~~~~~~%@", index, array[index]);
            
        });
        
        NSLog(@"done");
    });
}

實(shí)例七:dispatch_barrier_async

/**
 dispatch_barrier_async的使用
 
 dispatch_barrier_async是在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的任務(wù)等它執(zhí)行完成之后才會(huì)執(zhí)行
 
 只有當(dāng)這個(gè)隊(duì)列為自己創(chuàng)建的并發(fā)隊(duì)列(DISPATCH_QUEUE_CONCURRENT)時(shí)才會(huì)有這種效果
 執(zhí)行結(jié)果(一)為:
 dispatch_async2
 dispatch_async1
 dispatch_barrier_async
 dispatch_async3
 
 如果使用dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 根據(jù)官方文檔指出,這個(gè)時(shí)候的dispatch_barrier_async完全等同于dispatch_async
 執(zhí)行結(jié)果(二)為:
 dispatch_barrier_async
 dispatch_async3
 dispatch_async2
 dispatch_async1
 
 在使用DISPATCH_QUEUE_SERIAL串行隊(duì)列時(shí),完全就按照FIFO的順序執(zhí)行了
 執(zhí)行結(jié)果(三)為:
 2015-12-23 16:53:04.574 NSURLDemo[67716:3661098] dispatch_async1
 2015-12-23 16:53:05.577 NSURLDemo[67716:3661098] dispatch_async2
 2015-12-23 16:53:05.578 NSURLDemo[67716:3661098] dispatch_barrier_async
 2015-12-23 16:53:07.085 NSURLDemo[67716:3661098] dispatch_async3
 
 不僅有dispatch_barrier_async方法,還有dispatch_barrier_sync方法,兩個(gè)方法的區(qū)別是:
 dispatch_barrier_async當(dāng)他把這個(gè)barrier添加到隊(duì)列后,當(dāng)前隊(duì)列不用等待block的執(zhí)行返回
 而dispatch_barrier_sync需要等待block的內(nèi)容執(zhí)行完畢之后再繼續(xù)下面的執(zhí)行
 **/
- (void)test7
{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    //dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        
        [NSThread sleepForTimeInterval:3];
        
        NSLog(@"dispatch_async1~~~~~~~currentThread=%@~~~~~~~mainThread=%@",[NSThread currentThread], [NSThread mainThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        [NSThread sleepForTimeInterval:1];
        
        NSLog(@"dispatch_async2~~~~~~~currentThread=%@~~~~~~~mainThread=%@",[NSThread currentThread], [NSThread mainThread]);

        
    });
    
    dispatch_barrier_async(queue, ^{
        
        [NSThread sleepForTimeInterval:0.5];
        
        NSLog(@"dispatch_barrier_async~~~~~~~currentThread=%@~~~~~~~mainThread=%@",[NSThread currentThread], [NSThread mainThread]);

        
    });
    
    dispatch_async(queue, ^{
        
        [NSThread sleepForTimeInterval:1];
        
        NSLog(@"dispatch_async3~~~~~~~currentThread=%@~~~~~~~mainThread=%@",[NSThread currentThread], [NSThread mainThread]);

    });
}

實(shí)例八:dispatch_once一次使用函數(shù)

/**
 dispatch_once
 dispatch_once這個(gè)函數(shù),它可以保證整個(gè)應(yīng)用程序生命周期中某段代碼只被執(zhí)行一次!
 **/
- (void)test8
{
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        
        // code to be executed once
    });
}

實(shí)例九:dispatch_after延遲函數(shù)

/**
 dispatch_after
 有時(shí)候我們需要等個(gè)幾秒鐘然后做個(gè)動(dòng)畫(huà)或者給個(gè)提示,這時(shí)候可以用dispatch_after這個(gè)函數(shù):
 **/
-(void)test9
{
    double delayInSeconds = 2.0;
    
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        
        // code to be executed on the main queue after delay
    });
}

實(shí)例十:dispatch_set_target_queue轉(zhuǎn)換隊(duì)列

/**
  dispatch_set_target_queue
  通過(guò)dispatch_set_target_queue函數(shù)可以設(shè)置一個(gè)dispatch queue的優(yōu)先級(jí),或者指定一個(gè)dispatch source相應(yīng)的事件處理提交到哪個(gè)queue上。
  它會(huì)把需要執(zhí)行的任務(wù)對(duì)象指定到不同的隊(duì)列中去處理,這個(gè)任務(wù)對(duì)象可以是dispatch隊(duì)列,也可以是dispatch源。而且這個(gè)過(guò)程可以是動(dòng)態(tài)的,可以實(shí)現(xiàn)隊(duì)列的動(dòng)態(tài)調(diào)度管理等等。比如說(shuō)有兩個(gè)隊(duì)列dispatchA和dispatchB,這時(shí)把dispatchA指派到dispatchB:
  dispatch_set_target_queue(dispatchA, dispatchB);
  那么dispatchA上還未運(yùn)行的block會(huì)放到dispatchB上,然后由dispatchB來(lái)進(jìn)行管理運(yùn)行。
 **/

  dispatch_set_target_queue(serialQ, globalQ);

實(shí)例十一:dispatch_group_wait等待

//dispatch_group_wait來(lái)等待這些任務(wù)完成。若任務(wù)已經(jīng)全部完成或?yàn)榭眨瑒t直接返回,否則等待所有任務(wù)完成后返回。

//dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

-(void)test10
{
    
    dispatch_group_t group = dispatch_group_create();
    
    //注意queue為DISPATCH_QUEUE_CONCURRENT 和 DISPATCH_QUEUE_SERIAL時(shí)當(dāng)前的線(xiàn)程
    
    dispatch_queue_t queue = dispatch_queue_create([@"com.queue" UTF8String], DISPATCH_QUEUE_SERIAL);
    
    //每個(gè)dispatch_group_enter對(duì)應(yīng)一個(gè)dispatch_group_leave完成group內(nèi)所有的任務(wù)則發(fā)送通知
    
    //進(jìn)入group
    
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        
        NSLog(@"task1~~~~~currentThread=%@~~~~~mainThread=%@", [NSThread currentThread], [NSThread mainThread]);
        
        //離開(kāi)group
        
        dispatch_group_leave(group);
        
    });
    
    
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        
        NSLog(@"task2~~~~~currentThread=%@~~~~~mainThread=%@", [NSThread currentThread], [NSThread mainThread]);
        
        dispatch_group_leave(group);
        
    });
    
    NSLog(@"~~~~~~~~~~~~~~~~~~before group wait");
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"~~~~~~~~~~~~~~~~~~after group wait");
    
    dispatch_group_notify(group, queue, ^{
        
        NSLog(@"~~~~~~~~~~~~~allGroupFinish");
        
    });
}

補(bǔ)充:dispatch_sync(dispatch_get_main_queue(),...)造成死鎖的原因

當(dāng)這段代碼放在主線(xiàn)程里,也即dispatch_get_main_queue()中,執(zhí)行到sync時(shí)向dispatch_get_main_queue()插入同步thread,sync會(huì)等到里面的block執(zhí)行完成才返回。sync又在主隊(duì)列里面,是個(gè)串行隊(duì)列,sync是后面才加入的,前面一個(gè)是主線(xiàn)程,所以sync想執(zhí)行block必須等待前一個(gè)主線(xiàn)程執(zhí)行完成,而主線(xiàn)程卻在等待sync返回,才能執(zhí)行后續(xù)工作,從而造成死鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 多線(xiàn)程學(xué)習(xí)筆記-GCD 我把這篇文章所用到的代碼總結(jié)到這里->GCD項(xiàng)目總結(jié)下載地址-GCD-wxk可以下載參考 ...
    wxkkkkk閱讀 549評(píng)論 0 2
  • 一、簡(jiǎn)介在iOS所有實(shí)現(xiàn)多線(xiàn)程的方案中,GCD應(yīng)該是最有魅力的,因?yàn)镚CD本身是蘋(píng)果公司為多核的并行運(yùn)算提出的解決...
    MYS_iOS_8801閱讀 577評(píng)論 0 0
  • 前言 嘿嘿嘿,精品。 概述 全稱(chēng)是Grand Central Dispatch,可譯為“牛逼的中樞調(diào)度器”。純C語(yǔ)...
    Ostkaka丶閱讀 1,118評(píng)論 0 12
  • 一、基本概念 線(xiàn)程是用來(lái)執(zhí)行任務(wù)的,線(xiàn)程徹底執(zhí)行完任務(wù)A才能執(zhí)行任務(wù)B,為了同時(shí)執(zhí)行兩個(gè)任務(wù),產(chǎn)生了多線(xiàn)程 1、進(jìn)...
    空白Null閱讀 744評(píng)論 0 3
  • 你將老去,我將老去,無(wú)人幸免。 這個(gè)世界上,有些人有多冷漠,有些人就有多溫暖,希望你總是遇到,那些溫暖對(duì)你...
    一顆半糖閱讀 243評(píng)論 0 5