多線程

多線程(四種實現方式)
1.NSThread
2.NSObject
3.NSOperation/NSOperationQueue
4.GCD
進程:一個獨立運行的程序可以看做是一個進程。
線程:進程中的獨立代碼段。
通常一個程序只有一個進程,一個進程有1-n個線程(最少有一個主線程運行程序)
只有主線程的程序叫單線程程序,有主線程和子線程的程序稱為到線程程序

<h6>應用程序會默認為主線程分配1M的棧空間,默認為子線程分為512K的棧空間(分配必須是4K的整數倍)。主線程和子線程的棧區是相互獨立的。</h6>

<h6>主線程從main函數開始,所有的內容全部在自動釋放池管轄范圍內。子線程新開辟的區域在處理任務時,并沒有在自動釋放池的管轄范圍內,若在堆區開辟空間而不進行內存處理,會造成大量的泄漏(比如短時間內循環使用便利構造器等),所以人為開辟子線程必須要添加@autoreleasepool來管轄堆區的內容。主線程和子線程的堆區是共有的。

<1>NSThread

1.1使用示例對象來開辟子線程(手動開啟)

// 參數:方法的執行者、子線程需要執行的方法、傳遞的參數
NSThread *thread =[[NSThread alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
[thread start];// 必須手動開動才會執行子線程內容

// 取消子線程
// [thread cancel];
// 查看當前線程是否正在執行
NSLog(@"在執行%d",[thread isExecuting]);
// 查看當前線程是否取消
NSLog(@"已取消%d",[thread isCancelled]);



// 打印當前方法是否為主線程
NSLog(@"是否為主線程%d",[NSThread isMainThread]);
// 打印當前線程
NSLog(@"當前線程%@",[NSThread currentThread]);

1.2使用靜態方法來開辟子線程(自動開啟)

[NSThread detachNewThreadSelector:@selector(calculateAction) toTarget:self withObject:nil];

<2>NSObject

[self performSelectorInBackground:@selector(calculateAction) withObject:nil];
NSLog(@"主線程中開辟子線程代碼.....");

<3>NSOperationQueue:

此種多線程方式和NSOperation的兩個子類來結合使用,實現多線程方式
// 3.1
NSInvocationOperation *invocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
// 3.2
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
    
    [self calculateAction];
}];
// 創建管理隊列的兩個任務
NSOperationQueue *queue = [NSOperationQueue new];

// 設置最大并發數(同時執行任務的個數)
// 若最大并發數設置為1,那么任務將按照串行方式來執行
[queue setMaxConcurrentOperationCount:1];
// 將任務添加到隊列
[queue addOperation:invocation];
[queue addOperation:block];

<4>GCD

// 兩種隊列方式:并行隊列,串行隊列
// 并行隊列:所有任務并發執行,不分先后
// 串行隊列:所有任務依次執行,排隊完成

// GCD中,有三種隊列可以使用
// 主隊列:系統提供的單列,屬于串隊列
// 全局隊列:系統提供的單列,屬于并行隊列
// 自定義隊列:開發人員可以自定義選擇使用串行或者并行

4.1主隊列(int a = 10;主隊列的任務在主線程中執行)

dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 給隊列添加任務
dispatch_async(mainQueue, ^{
    
    NSLog(@"第一個任務%d",[NSThread isMainThread]);
});

dispatch_async(mainQueue, ^{
        
    NSLog(@"第二個任務%d",[NSThread isMainThread]);
});

dispatch_async(mainQueue, ^{
    
    NSLog(@"第三個任務%d",[NSThread isMainThread]);
});

4.2全局隊列:并行隊列,會開辟子線程,但是其管理的任務不一定只在子線程執行,也會添加到主線程執行

// 1.隊列優先級
// 2.空余參數,以后添加作用   
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 必須先執行我(第一個加入隊列時,才會優先執行此任務(在主線程中執行),否則和其他三個任務一樣無法確認哪個先執行)
dispatch_barrier_sync(globalQueue, ^{// 此處是_sync不是_async
    
    NSLog(@"優先執行的代碼......%d",[NSThread isMainThread]);
});

// 向隊列中添加任務(三個任務在子線程中同步執行,打印結果沒有固定的先后順序)
dispatch_async(globalQueue, ^{
    
    NSLog(@"第一個任務%d",[NSThread isMainThread]);
});

dispatch_async(globalQueue, ^{
    
    NSLog(@"第二個任務%d",[NSThread isMainThread]);
});

dispatch_async(globalQueue, ^{
    
    NSLog(@"第三個任務%d",[NSThread isMainThread]);
});
延時啟動任務
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), globalQueue, ^{
    
    NSLog(@"出來吧,神龍!");
});
反復執行n次的任務
dispatch_apply(3, globalQueue, ^(size_t time) {
    
    NSLog(@"第%zu次",time);
});
函數
void function(void * string);
void function(void * string){

    NSLog( @"%s",string);
}
添加此函數到隊列
// 1.要把任務添加到的隊列
// 2.函數的參數
// 3.函數
dispatch_async_f(globalQueue, "fall in love with", function);

4.3自定義隊列

串行隊列

// 1.給隊列一個標簽,后續可以獲取隊列的標簽來操作
dispatch_queue_t serialQueue = dispatch_queue_create("com.lanou3g.mySerialQueue", DISPATCH_QUEUE_SERIAL);
// async和sync添加任務的區別在于是否同時執行block和外部的代碼(即子線程和主線程是否同時執行),前者會同時執行,后者則是順序執行
dispatch_async(serialQueue, ^{
   
    NSLog(@"第一個任務%d",[NSThread isMainThread]);
});
NSLog(@"1");

dispatch_async(serialQueue, ^{
    
    NSLog(@"第二個任務%d",[NSThread isMainThread]);
});
NSLog(@"2");

dispatch_async(serialQueue, ^{
    
    NSLog(@"第三個任務%d",[NSThread isMainThread]);
});
NSLog(@"3");

并行隊列

dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

/*
dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第一個任務%d",[NSThread isMainThread]);
});

dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第二個任務%d",[NSThread isMainThread]);
});

dispatch_async(concurrentQueue, ^{
    
    NSLog(@"第三個任務%d",[NSThread isMainThread]);
});
*/
/*
// 分組:使用一個組來操作一組任務
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, concurrentQueue, ^{
   
    NSLog(@"第一個分組任務%d",[NSThread isMainThread]);
});

dispatch_group_async(group, concurrentQueue, ^{
    
    NSLog(@"第二個分組任務%d",[NSThread isMainThread]);
});

dispatch_group_async(group, concurrentQueue, ^{
    
    NSLog(@"第三個分組任務%d",[NSThread isMainThread]);
});

// 等待分組中所有任務執行完畢后再執行此任務(必須寫在后面)
dispatch_group_notify(group, concurrentQueue, ^{
    
    NSLog(@"撩漢終結者,kingStar");
});

*/

子線程回到主線程的演示

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 添加子任務
dispatch_async(globalQueue, ^{
   
    [self calculateAction];
});

// 主、全局無論哪種隊列都遵循FIFO(先進先出)
用來耗時的計算(多線程測試的使用)
- (void)calculateAction{
    // 讓子線程睡10秒再干活
    [NSThread sleepForTimeInterval:2];

    // 當子線程中出現對象類型時,需要使用自動釋放池包裹對應的代碼
    @autoreleasepool {
    
        int sum = 0;
        for (int i = 0; i < 655350000; i++ ){
            sum += i;
        }
        NSLog(@"%d",sum);

        // 子線程回到主線程的第一種方式
        // 1.執行在主線程的方法
        // 2.傳遞參數
        // 3.是否等到完成后操作
         NSNumber *nsnum = [NSNumber numberWithInt:sum ];
         [self performSelectorOnMainThread:@selector(mainAction:) withObject:nsnum waitUntilDone:YES];
        // 打印當前方法是否為主線程
        NSLog(@"===%d",[NSThread isMainThread]);
        // 打印當前線程
        NSLog(@"===%@",[NSThread currentThread]);
         
        // 子線程回到主線程的第二種方式
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%d",sum);
            NSLog(@"%d",[NSThread isMainThread]);
        });
    }
}

// 子線程回到主線程的第一種方式調用的mainAction:方法
- (void)mainAction:(NSNumber *)sum{

    NSLog(@"計算的結果為%d",[sum intValue]);
    NSLog(@"當前線程是否為主線程%d",[NSThread isMainThread]);
}

線程按照生命周期分為兩種
脫離線程:線程使用完畢后,即被銷毀,不可在喚醒
非脫離線程:線程任務執行時,始終處于被喚醒的狀態,一旦收集到任務,馬上啟動,生命周期長,可被喚醒。

// 多線程操作(兩隊人購票)

// 初始化開辟空間
self.lock = [NSLock new];

// 多線程容易出現線程互斥問題

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(globalQueue, ^{
    
   // 第一隊人在購票
    for (int i = 0; i < 1000; i++) {
        [self buyTicket:@"王沖"];
    }
});

dispatch_async(globalQueue, ^{
    
    // 第二隊人在購票
    for (int i = 0; i < 1000; i++) {
        [self buyTicket:@"JonKing"];
    }
});

// atomic和nonatomic實現
// atomic是線程安全的,使用@synchronized進行對對象的加鎖操作,但是只能是對象類型。
// nonatomic是線程不安全的,沒有對數據做任何處理。
// 舉例代碼:下列代碼的意思:當訪問到self對象時,寫在大括號內部的代碼會被保證同一時間只允許一個任務在訪問。
@synchronized(self) {
    // 操作
}
購票方法
int count = 5000;
- (void)buyTicket:(NSString *)str{

    // 每次遭遇訪問時,要進行加鎖操作
    [self.lock lock];


    count -- ;// 模擬每次買票造成的結果,票數減一

    NSLog(@"%@購的一張票,剩余%d張票",str,count);


    // 資源訪問完畢時,進行解鎖讓下一個任務訪問
    [self.lock unlock];

}

其他資料 作者 YotrolZ 多線程的實現方案(4種)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • #import "ViewController.h" @interface ViewController () @...
    艾克12138閱讀 266評論 0 0
  • #import "ViewController.h" @interface ViewController () @...
    艾克12138閱讀 225評論 0 0
  • iOS 多線程系列 -- 基礎概述iOS 多線程系列 -- pthreadiOS 多線程系列 -- NSThrea...
    shannoon閱讀 885評論 0 2
  • 一、前言 本篇博文介紹的是iOS中常用的幾個多線程技術: NSThread GCD NSOperation 由于a...
    和玨貓閱讀 588評論 0 1
  • 無名的卑微的崇拜者呀 為何你要把那恥辱愉快的信仰 不羈的狂放的邪教徒呀 你的快樂的毒酒 曾毒害了一個純潔而高傲的心...
    答案在風中飛揚閱讀 371評論 0 1