iOS-多線程-總結(jié)

容易混淆的概念(隊列、線程、串行并行、同步異步、任務(wù))

任務(wù):代碼塊(一行或多行),也叫block
隊列:管理任務(wù)的一個線性數(shù)據(jù)結(jié)構(gòu),有兩種:串行和并行。區(qū)別是是否有開啟新線程的能力
同步異步:說的是任務(wù)的執(zhí)行方式。同步指的是阻塞當(dāng)前線程等待該任務(wù)執(zhí)行完才繼續(xù)下面的任務(wù)。

//是否開辟新線程不是隊列決定的,是由系統(tǒng)線程池決定的。經(jīng)驗:
1.串行+同步不創(chuàng)建,串行+異步會創(chuàng)建(主隊列除外)
2.并行+同步不創(chuàng)建,并行+異步可能會創(chuàng)建

// dispatch_sync官方原文
This function submits a block to the specified dispatch queue for synchronous execution. Unlike dispatch_async, this function does not return until the block has finished. Calling this function and targeting the current queue results in deadlock.
Unlike with dispatch_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As a performance optimization, this function executes blocks on the current thread whenever possible, with one exception: Blocks submitted to the main dispatch queue always run on the main thread.
//大意:
1、是在當(dāng)前線程上指定隊列中同步地執(zhí)行任務(wù)。會阻塞當(dāng)前線程等待該任務(wù)執(zhí)行完才繼續(xù)下面的任務(wù),主隊列除外。
2、是立即返回?zé)o需等待的,所以不需要block copy操作

iOS多線程方案有四種:pthread(C),NSThread(OC), GCD(C,自動管理),NSOperation(GCD的封裝)。常用的是自動管理線程生命周期的GCD和NSOperation,其中GCD有以靈活性居先。

一、GCD

1、四種基本組合

串行+同步

    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
// 打印1,崩潰在__DISPATCH_WAIT_FOR_QUEUE__,死鎖

串行+異步

    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2-1-%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-2-%@", [NSThread currentThread]);
    });
    NSLog(@"3");
// 打印1,3,2-1-main,2-2-main

并行+同步

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    NSLog(@"1");
    dispatch_sync(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"2-%@", [NSThread currentThread]);
    });
    NSLog(@"3");
// 打印1,2-main,3

并行+異步

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2-1-%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-2-%@", [NSThread currentThread]);
    });
    NSLog(@"3");
// 打印1,3,2-1-num7,2-2-num4,開啟了新線程

總結(jié):串行+同步會死鎖。并行+異步可開啟新線程。

2、復(fù)雜情況

不同隊列嵌套

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"1-%@", [NSThread currentThread]);
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2-%@", [NSThread currentThread]);
        });
        NSLog(@"3-%@", [NSThread currentThread]);
    });
    NSLog(@"4-%@", [NSThread currentThread]);
// 打印4-main,1-num5,2-mian,3-num5。1/4順序不定

相同隊列嵌套

    dispatch_queue_t queue = dispatch_queue_create("com.xx", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"#--0");
        dispatch_sync(queue, ^{
            NSLog(@"#--1");
        });
        NSLog(@"#--2");
    });
// 打?。?,然后死鎖
    dispatch_queue_t queue = dispatch_queue_create("com.xx", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"#--0,%@",[NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"#--1,%@",[NSThread currentThread]);
        });
        NSLog(@"#--2,%@",[NSThread currentThread]);
    });
//打?。?-main 2-main 1-null,主隊列特殊,全是main

Exam 1

    NSLog(@"#--test");
    __block int i = 0;
    dispatch_queue_t queue0 = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);
    while (i < 1000) {
        //dispatch_queue_t queue = dispatch_queue_create(0, 0);
        dispatch_async(queue0, ^{
            //NSLog(@"#--thread=%@",[NSThread currentThread]);
            i++;
        });
    }
    NSLog(@"#--i=%d",i);
// >=1000
3、api使用

1.dispatch_after

// 適用于不太精準(zhǔn)的延遲執(zhí)行
    NSLog(@"start");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });

dispatch_after不像NSTimer和performSelector:afterDelay一樣需要runloop支持,但dispatch_after 并不是在指定時間后執(zhí)行任務(wù),而是在指定時間之后才將任務(wù)提交到隊列中,這個延遲的時間是不精確的。

  1. dispatch_once
static TheClass *instance = nil;
+ (instance)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[TheClass alloc] init];
    });
    return instance;
}
  1. dispatch_apply
    同步執(zhí)行按照指定的次數(shù)提交到指定的隊列中的任務(wù),可以將迭代任務(wù)轉(zhuǎn)換為并發(fā)任務(wù),迭代任務(wù)繁重時用它效率成倍提升。
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t iteration) {
        NSLog(@"%zu-%@", iteration, [NSThread currentThread]);
    });
// 可創(chuàng)建多個線程
  1. dispatch_barrier_async
    提供一個 “柵欄” 將兩組異步執(zhí)行的任務(wù)分隔開,保證先于柵欄方法提交到隊列的任務(wù)全部執(zhí)行完成之后,然后開始執(zhí)行將柵欄任務(wù),等到柵欄任務(wù)執(zhí)行完成后,該隊列便恢復(fù)原本執(zhí)行狀態(tài)。只適用于自定義并發(fā)隊列,全局并發(fā)隊列和串行隊列無意義。
    dispatch_queue_t queue = dispatch_queue_create("thread", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"test1");
    });
    dispatch_async(queue, ^{
        NSLog(@"test2");
    });
    dispatch_async(queue, ^{
        NSLog(@"test3");
    });
    
    dispatch_barrier_sync(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"barrier");
    });
    NSLog(@"aaa");
    dispatch_async(queue, ^{
        NSLog(@"test4");
    });
    NSLog(@"bbb");
    dispatch_async(queue, ^{
        NSLog(@"test5");
    });
    dispatch_async(queue, ^{
        NSLog(@"test6");
    });
//打印 test1,test3,test2,barrier,aaa,bbb,test4,test5,test6
//異步 aaa,test1,bbb,test2,test3,barrier,test4,test5,test6
// 執(zhí)行的順序,同一線程內(nèi)同步有序,異步無序和執(zhí)行任務(wù)效率有關(guān)。不同線程只和任務(wù)效率有關(guān)。

5.dispatch_group

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"1");
//    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        dispatch_async(queue, ^{
            sleep(1); //這里線程睡眠1秒鐘,模擬異步請求
            NSLog(@"2");
//            dispatch_group_leave(group);
        });
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"3");
    });
//    dispatch_group_wait(group, DISPATCH_TIME_NOW);
    NSLog(@"4");
    dispatch_async(queue, ^{
        NSLog(@"5");
    });
}
// 打印1,4,3,5,2(3,5順序不定)。
//dispatch_group_enter/leave: notify只負(fù)責(zé)group里面的任務(wù)執(zhí)行完,如果group里面嵌套有異步任務(wù),是不會管他就直接返回的。這時就需要enter/leave了,成對出現(xiàn)的,enter多了會阻塞,leave多了會崩潰。
// dispatch_group_wait:阻塞group到某個時刻,然后釋放繼續(xù)執(zhí)行下面的任務(wù),參數(shù)DISPATCH_TIME_NOW表示不阻塞,相當(dāng)于沒有,參數(shù)DISPATCH_TIME_FOREVER表示永遠(yuǎn)阻塞,還可以自定義為3秒后的時刻,表示阻塞3秒,之后放開。打開wait并設(shè)置time為FOREVER后,打印為1,4,3,5,2,3在5前面

6、dispatch_semaphore
GCD的鎖機(jī)制,iOS線程同步除了os_unfair_lock 和
OSSpinLock之外,dispatch_semaphore的性能是很好的極力推薦使用。
dispatch_semaphore_create:根據(jù)傳入的初始值創(chuàng)建一個信號量。
不可傳入負(fù)值。運(yùn)行過程中,若內(nèi)部值為負(fù)數(shù),則這個值的絕對值便是正在等待資源的線程數(shù)。
dispatch_semaphore_wait:信號量 -1。 -1 之后的結(jié)果值小于 0 時,線程阻塞,并以 FIFO 的方式等待資源。
dispatch_semaphore_signal:信號量 +1。 +1 之后的結(jié)果值大于 0 時,以 FIFO 的方式喚醒等待的線程。
一般用來:控制最大并發(fā)數(shù)、數(shù)組多線程安全、阻塞異步任務(wù)

// 控制最大并發(fā)數(shù)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 10; i++) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_async(queue, ^{
            NSLog(@"%d-%@", i, [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
    }
// YYKit 中 YYThreadSafeArray 的實現(xiàn)(無法多讀)
// 通過宏定義對代碼塊進(jìn)行加鎖操作
#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(_lock);
// id obj = array[idx];
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
    LOCK(id o = [_arr objectAtIndexedSubscript:idx]); return o;
}
// array[idx] = obj;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx {
    LOCK([_arr setObject:obj atIndexedSubscript:idx]);
}

7、dispatch_source
用GCD的函數(shù)指定一個希望監(jiān)聽的系統(tǒng)事件類型,再指定一個捕獲到事件后進(jìn)行邏輯處理的閉包或者函數(shù)作為回調(diào)函數(shù),然后再指定一個該回調(diào)函數(shù)執(zhí)行的Dispatch Queue即可,當(dāng)監(jiān)聽到指定的系統(tǒng)事件發(fā)生時會調(diào)用回調(diào)函數(shù),將該回調(diào)函數(shù)作為一個任務(wù)放入指定的隊列中執(zhí)行。
https://blog.csdn.net/hailideboke/article/details/78711514

4、靈活運(yùn)用

1.阻塞異步任務(wù)的幾種方式
group、barrier、semaphore

二、NSOperation

三、其他相關(guān)問題

1、線程與runloop
延時執(zhí)行任務(wù):
dispatch_after 比 NSTimer 優(yōu)秀,因為他不需要指定 Runloop 的運(yùn)行模式。 dispatch_after 比 performSelector:afterDelay: 優(yōu)秀,因為它不需要 Runloop 支持。

iOS定時器:
NSTimer:
dispatch_after:

2、線程與數(shù)據(jù)安全
多讀單寫-barrier、單讀單寫-semaphore

3、如何取消GCD任務(wù)
dispatch_block_cancel、外部變量控制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容