iOS學(xué)習(xí)筆記11-多線程入門

一、iOS多線程

iOS多線程開發(fā)有三種方式:
  1. NSThread
  1. NSOperation
  2. GCD

iOS在每個(gè)進(jìn)程啟動(dòng)后都會(huì)創(chuàng)建一個(gè)主線程更新UI要在主線程上,所以也稱為UI線程,是其他線程的父線程。

線程和進(jìn)程的區(qū)別傻傻分不清楚:
  • 線程(thread):用于指代獨(dú)立執(zhí)行的代碼段。
  • 進(jìn)程(process):用于指代一個(gè)正在運(yùn)行的可執(zhí)行程序,它可以包含多個(gè)線程。
多線程加載圖片

二、NSThread

NSThreadhi輕量級(jí)的多線程開發(fā),需要自己管理線程生命周期

創(chuàng)建線程主要實(shí)現(xiàn)方法:
/* 直接將操作添加到新線程中并執(zhí)行,該方法無(wú)法拿到線程對(duì)象 */
+ (void)detachNewThreadSelector:(SEL)selector /* 方法名 */
                       toTarget:(id)target /* 調(diào)用對(duì)象 */
                     withObject:(id)argument; /* 參數(shù) */
/* 創(chuàng)建線程對(duì)象,初始化線程任務(wù),調(diào)用start方法啟動(dòng)線程 */
- (instancetype)initWithTarget:(id)target /* 調(diào)用對(duì)象 */
                      selector:(SEL)selector /* 方法名 */
                        object:(id)argument;/* 參數(shù) */
實(shí)際使用:
/* 創(chuàng)建一個(gè)線程,初始化任務(wù),創(chuàng)建線程并不會(huì)啟動(dòng)線程 */
NSThread *thread = [[NSThread alloc] initWithTarget:self 
                                           selector:@selector(loadImage) 
                                             object:nil];
[thread start];//啟動(dòng)線程

/* 直接將操作添加到新線程并啟動(dòng)線程 */
[NSThread detachNewThreadSelector:@selector(loadImage) 
                         toTarget:self 
                       withObject:nil];
  • 每個(gè)線程的實(shí)際執(zhí)行順序并不一定按啟動(dòng)順序執(zhí)行
  • 如果是單核CPU,多線程是并發(fā),分時(shí)間片切換執(zhí)行不同線程,多核CPU的多線程才是真正的并行運(yùn)算。
線程狀態(tài)分為:
  • isExecuting(正在執(zhí)行)
  • isFinished(已經(jīng)完成)
  • isCancellled(已經(jīng)取消)
下面是常用方法來(lái)控制線程:
/* 讓線程休眠 */
+ (void)sleepUntilDate:(NSDate *)date;/* 讓當(dāng)前執(zhí)行線程休眠到某個(gè)時(shí)間 */
+ (void)sleepForTimeInterval:(NSTimeInterval)time;/* 讓當(dāng)前執(zhí)行線程休眠固定多少秒 */
/* 終止線程 */
+ (void)exit;
/* 停止線程,注意在主線程中調(diào)用僅僅只是設(shè)置線程狀態(tài),不會(huì)立刻停止線程 */
- (void)cancel;
實(shí)例:
NSThread *thread = threads[i];
//判斷線程是否完成,如果沒(méi)有完成則設(shè)置為取消狀態(tài)
//注意設(shè)置為取消狀態(tài)僅僅是改變了線程狀態(tài)而言,并不能立刻終止線程
if ( !thread.isFinished ) {
    [thread cancel];
}
//線程休眠2秒
[NSThread sleepForTimeInterval:2.0];

我們知道了控制單個(gè)線程,怎么在線程之間進(jìn)行通信呢?

下面是線程間通信的常用方法:
/* 在后臺(tái)執(zhí)行一個(gè)操作,本質(zhì)就是重新創(chuàng)建一個(gè)線程執(zhí)行當(dāng)前方法 */
- (void)performSelectorInBackground:(SEL)aSelector
                         withObject:(id)arg;
/* 在指定的線程上執(zhí)行一個(gè)方法,需要用戶創(chuàng)建一個(gè)線程對(duì)象 */
- (void)performSelector:(SEL)aSelector
               onThread:(NSThread *)thr
             withObject:(id)arg
          waitUntilDone:(BOOL)wait;
/* 在主線程上執(zhí)行一個(gè)方法 */
- (void)performSelectorOnMainThread:(SEL)aSelector
                         withObject:(id)arg
                      waitUntilDone:(BOOL)wait;

三、NSOperation

只要將NSOperation放入NSOperationQueue線程隊(duì)列中,就會(huì)啟動(dòng)執(zhí)行。
NSOperationQueue負(fù)責(zé)管理、執(zhí)行所有的NSOperation,這樣更加容易管理線程總數(shù)控制線程之間的依賴

NSOperation是個(gè)基類,我們直接使用的是它的子類:
  • NSInvocationOperation:調(diào)用方法SEL的方式執(zhí)行線程可以使用它
  • NSBlockOperation:調(diào)用Block的方式執(zhí)行線程可以使用它
下面是使用實(shí)例:
//創(chuàng)建Invocation線程
NSInvocationOperation *invocationOperation = 
        [[NSInvocationOperation alloc] initWithTarget:self 
                                             selector:@selector(loadImage) 
                                               object:nil];
//注意如果直接調(diào)用start方法,則此操作會(huì)在主線程中調(diào)用
// [invocationOperation start];//一般不會(huì)這么操作,而是添加到NSOperationQueue中
//創(chuàng)建線程隊(duì)列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 5;//設(shè)置最大并發(fā)線程數(shù)
//注意添加到線程隊(duì)列后,隊(duì)列里的線程就會(huì)開始執(zhí)行
[operationQueue addOperation:invocationOperation];

//創(chuàng)建Block線程
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    [self loadImage:[NSNumber numberWithInt:0]];
}];
//添加進(jìn)線程隊(duì)列
[operationQueue addOperation:blockOperation];

如果覺(jué)得添加NSBlockOperation線程麻煩,還有個(gè)簡(jiǎn)單的方法,是NSOperationQueue的對(duì)象方法:

// 快捷添加NSBlockOperation
[operationQueue addOperationWithBlock:^{
    [self loadImage:[NSNumber numberWithInt:0]];
}];

我說(shuō)過(guò)NSOperationQueue可以控制線程之間的依賴,這是怎么一回事呢?想象一種場(chǎng)景,加載多個(gè)圖片,但我想最后一張圖片一定要先加載,其他圖片加載的前提就是最后一張圖片要加載完成,這時(shí)候就可以使用依賴了。非常簡(jiǎn)單。

多圖片加載實(shí)例:
- (void)loadImageWithMultiThread{
    int count = ROW_COUNT*COLUMN_COUNT;
    //創(chuàng)建線程隊(duì)列
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc]init];
    operationQueue.maxConcurrentOperationCount = 5;//設(shè)置最大并發(fā)線程數(shù)
    //創(chuàng)建加載最后一張圖片的線程
    NSBlockOperation *lastBlockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [self loadImage:[NSNumber numberWithInt:(count-1)]];
    }];
    //創(chuàng)建多個(gè)線程用于下載其他圖片
    for (int i=0; i<count-1; ++i) {
        //創(chuàng)建多線程操作
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            [self loadImage:[NSNumber numberWithInt:i]];
        }];
        //設(shè)置依賴操作為最后一張圖片加載操作,只有最后一張圖片加載完成,其他圖片才開始陸續(xù)加載
        [blockOperation addDependency:lastBlockOperation];
        [operationQueue addOperation:blockOperation];
    }
    //將最后一個(gè)圖片的加載線程加入線程隊(duì)列
    [operationQueue addOperation:lastBlockOperation];
}
依賴的應(yīng)用-多圖片加載-最后一個(gè)圖片先加載,其他才開始加載

四、GCD

GCD中也有一個(gè)類似于NSOperationQueue的隊(duì)列,GCD統(tǒng)一管理整個(gè)隊(duì)列中的任務(wù),GCD是C語(yǔ)言下的框架。

GCD中的隊(duì)列分為并行隊(duì)列和串行隊(duì)列:
  • 串行隊(duì)列(serial):只有一個(gè)線程,加入到隊(duì)列中的操作按添加順序依次執(zhí)行。
  • 并發(fā)隊(duì)列(concurrent):有多個(gè)線程,操作進(jìn)來(lái)之后它會(huì)將這些隊(duì)列安排在可用的處理器上,同時(shí)保證先進(jìn)來(lái)的任務(wù)優(yōu)先處理,但不是順序的。

其實(shí)在GCD中還有一個(gè)特殊隊(duì)列就是主隊(duì)列,用來(lái)執(zhí)行主線程上的操作任務(wù)。

GCD執(zhí)行方式也分為異步執(zhí)行和同步執(zhí)行:
  • dispatch_async(異步執(zhí)行) :不管隊(duì)列中的任務(wù)執(zhí)行完還是沒(méi)執(zhí)行完,直接將任務(wù)追加到隊(duì)列
  • dispatch_sync(同步執(zhí)行) : 等隊(duì)列中的任務(wù)執(zhí)行完,再將任務(wù)追加到隊(duì)列
串行隊(duì)列和并發(fā)隊(duì)列的創(chuàng)建:
/*創(chuàng)建一個(gè)隊(duì)列
 第一個(gè)參數(shù):隊(duì)列名稱
 第二個(gè)參數(shù):隊(duì)列類型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT并發(fā)
 注意:GCD的queue不是指針類型
*/
dispatch_queue_t serialQueue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

/* 使用dispatch_get_global_queue() 方法取得一個(gè)全局的并發(fā)隊(duì)列 */
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

隊(duì)列的異步執(zhí)行和同步執(zhí)行:
//異步執(zhí)行隊(duì)列任務(wù),第一個(gè)參數(shù)是隊(duì)列,第二個(gè)參數(shù)是任務(wù)Block
dispatch_async(queue, ^{
    [self loadImage:[NSNumber numberWithInt:i]];
}); 
//同步執(zhí)行隊(duì)列任務(wù),第一個(gè)參數(shù)是隊(duì)列,第二個(gè)參數(shù)是任務(wù)Block
dispatch_sync(queue, ^{
    [self loadImage:[NSNumber numberWithInt:i]];
}); 
  • 在GDC中一個(gè)操作是多線程執(zhí)行還是單線程執(zhí)行,取決于當(dāng)前隊(duì)列類型和執(zhí)行方法,只有隊(duì)列類型為并行隊(duì)列并且使用異步方法執(zhí)行時(shí)才能在多個(gè)線程中并發(fā)執(zhí)行。
  • 串行隊(duì)列可以按順序執(zhí)行,并行隊(duì)列的異步方法無(wú)法確定執(zhí)行順序。
  • 更新UI界面最好采用同步方法,其他操作采用異步方法。
GCD的其他任務(wù)執(zhí)行方法:
/* 重復(fù)執(zhí)行某個(gè)任務(wù),為了不阻塞線程可以使用dispatch_async()包裝一下再執(zhí)行 */
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));

/* 單次執(zhí)行一個(gè)任務(wù),此方法中的任務(wù)只會(huì)執(zhí)行一次,重復(fù)調(diào)用也沒(méi)辦法重復(fù)執(zhí)行(單例模式中常用此方法)*/
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

/* 常用:延遲delayInSeconds秒后在隊(duì)列queue執(zhí)行block操作 */
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_queue_t queue, dispatch_block_t block);

五、線程同步

為什么需要線程同步?因?yàn)橐鉀Q多線程的資源搶奪的問(wèn)題

1.NSLock同步鎖

//初始化鎖對(duì)象
NSLock *myLock = [[NSLock alloc] init];
//加鎖
[myLock lock];//加鎖后,下面的代碼只能有一個(gè)線程進(jìn)入執(zhí)行
if (_imageNames.count > 0) {
    name = [_imageNames lastObject];
    [_imageNames removeObject:name];
}
//使用完解鎖
[myLock unlock];

2.@synchronized代碼塊

//線程同步
@synchronized(self){
    if (_imageNames.count > 0) {
        name = [_imageNames lastObject];
        [_imageNames removeObject:name];
    }
}

線程同步就不細(xì)講了,這是一個(gè)大塊知識(shí)。

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

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

  • 原文:http://www.cocoachina.com/ios/20170707/19769.html 本文主要...
    冬的天閱讀 2,433評(píng)論 0 12
  • Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么? 1...
    AlanGe閱讀 1,929評(píng)論 0 17
  • 多線程 在iOS開發(fā)中為提高程序的運(yùn)行效率會(huì)將比較耗時(shí)的操作放在子線程中執(zhí)行,iOS系統(tǒng)進(jìn)程默認(rèn)啟動(dòng)一個(gè)主線程,用...
    郭豪豪閱讀 2,726評(píng)論 0 4
  • 什么是進(jìn)程? 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序。 每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存...
    珍此良辰閱讀 1,415評(píng)論 1 5
  • 學(xué)習(xí)多線程,轉(zhuǎn)載兩篇大神的帖子,留著以后回顧!第一篇:關(guān)于iOS多線程,你看我就夠了 第二篇:GCD使用經(jīng)驗(yàn)與技巧...
    John_LS閱讀 741評(píng)論 0 3

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