天天看點

iOS多線程技術---pthread、NSThread、NSOperation、GCD

多線程技術

process程序:在系統中運作的一個應用程式;每個程序都是獨立的;有專有的記憶體空間

thread線程:程式的一段執行序列,程序的不部分;

             特點:1、程序隻配置設定記憶體空間,并不執行任務

                         2、每個程序至少有一個線程,該線程叫做主線程

                         3、線程是程序的基本執行單元,程序的所有任務都是線上程中執行

                         4、每個線程中得任務的執行都是串行的

多線程并發:一個程序中多條線程并發執行任務;

            特點:   1、提高程式的執行效率,提高資源使用率

                        2、同一時間,CPU隻能處理一條線程

                        3、多線程并發時,CPU在多條線程間快速切換

                        4、線程切換的 速度很快,就造成了多線程的并發執行

                        5、開啟線程需要記憶體空間,線程太多會造成排程消耗太多資源

                        6、線程過多會降低每條線程被排程的頻率(線程執行效率降低)

多線程的應用:

        主線程:顯示重新整理UI界面、處理UI事件;耗時任務(如下載下傳)放在子線程中

        判定方法在哪個線程中執行:NSLog(@“目前的線程:”,[NSThread currentThread]);

四種多線程技術:pthread   NSThread   GCD  NSOperation

1.pthread: 

      基于c語言的API ,可編寫多平台應用,使用難度大,需要手動建立銷毀線程,可自定義功能少

pthread_t pthread;

void *task(void *data){

NSLog(@“目前子線程:%@“,NSThread currentThread];

return 0;

}

pthread_create(&pthread,NULL,task,NULL);

2.NSThread: 

  • 基于OC,相對于pthread使用簡單,面向對象
  • 缺點:需要自己管理線程的生命周期,線程同步,加鎖、開鎖,管理多個線程比較困難

手動建立線程的三種方法:

1、NSThread執行個體方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);(隻建立)

2、NSThread類方法:+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; (建立并運作)

3、NSThread執行個體方法:- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);

注:selector: 線程執行的方法,這個selector隻能由一個參數,而且沒有傳回值

        object: 傳給target的唯一參數,也可以為nil

        target: selector消息發送的對象 隻有第一個能設定名字、優先級

線程阻塞(延遲):

  • // sleep⽅法⼀:開始睡眠5秒鐘

[NSThread sleepForTimeInterval:5];

  • // sleep⽅法二:

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3

[NSThread sleepUntilDate:date];

強制停止線程:

[NSThread exit];

其他常用方法:

  • +(NSThread *)mainThread; //獲得主線程
  • -(BOOL)isMainThread;//判斷是否是主線程
  • +(double)threadPriority;//擷取線程優先級    優先級取值範圍0.0~1.0 值越大優先級越高
  • +(BOOL)setThreadPriority:(double)p;//設定優先級
  • -(NSString*)name;//擷取線程名字
  • -(void)setName:(NSString*)n;//設定線程名字

線程的狀态:

  • New(建立) -> Runnable(就緒) --> cpu開始排程目前線程 ---> Running(運作) ---> 調用 sleep方法 ---> Blocked(阻塞) ---> 移除線程池 ---> sleep執行完畢/到時 ---> 進入線程 池 ---> Runnable(就緒)
  • 如果線程任務執行完畢/異常/強制退出 ---> Dead(死亡)

加鎖:

  • 盡量避免使用@synchronized,也就是避免多個線程通路同一個資源,因為有了加鎖、 解鎖需要消耗比較大的cpu資源 
  • 加鎖的前提:多個線程同時通路同一個資源的時候才需要加鎖(互斥鎖) 
  • 線程同步:多條線程在同一條線上執行(按順序地執行任務) 
  • 盡量講加鎖、資源搶奪的業務邏輯交割伺服器端處理,減少移動用戶端的壓力

synchronized關鍵字

1、synchronized關鍵字的作用域有二種:

1)是某個對象執行個體内,synchronized aMethod(){}可以防止多個線程同時通路這個對象的synchronized方法(如果一個對象有多個synchronized方法,隻要一個線程通路了其中的一個synchronized方法,其它線程不能同時通路這個對象中任何一個synchronized方法)。這時,不同的對象執行個體的 synchronized方法是不相幹擾的。也就是說,其它線程照樣可以同時通路相同類的另一個對象執行個體中的synchronized方法;

2)是某個類的範圍,synchronized static aStaticMethod{}防止多個線程同時通路這個類中的synchronized static 方法。它可以對類的所有對象執行個體起作用

2、除了方法前用synchronized關鍵字,synchronized關鍵字還可以用于方法中的某個區塊中,表示隻對這個區塊的資源實行互斥通路。用法是: synchronized(this){},它的作用域是目前對象;

原子和非原子屬性:

  • nonatomic: 非原子 —> 不會在set方法中加鎖,這個是推薦方式,會占用資源少
  • atomic:原子 —> 在set方法中加鎖,防止多個線程同時執行set方法

線程間的通訊:

1.在一個程序中,線程往往不是孤立存在的,多個線程之間需要經常進行通信 

2.線程間通訊的方式:

  • 一個線程傳遞資料給另一個線程
  • 在一個線程中執行完特定任務後,轉到另一個線程繼續執行任務

子線程傳回主線程的兩種方法:

 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait

***********************************************************************************************

3. GCD

 并行隊列隻有在執行多個異步任務的時候展現出來并發現象,因為線上程快速切換時展現出并行;

 同步執行的任務 會緻使後面的任務送出阻塞;

GCD:Grand Central Dispath 大中央排程

  • GCD的基本思想就是将操作(任務)放在隊列中去執行
  • 隊列負責排程任務執行所在的線程以及具體的執行時間
  • 隊列的特點是先進先出,新添加至隊列的操作(任務)都會排在隊尾

  GCD的函數都是以dispatch開頭的,dispatch的意思是“配置設定、排程”  

  • 串行隊列中的任務會按順序執行
  • 并行隊列中的任務通常會并發執行,而且無法确定任務的執行順序

  dispatch_async表示異步操作,異步操作會新開辟線程來執行任務,而且無法确定任務的執行順序 dispatch_sync表示同步操作,同步操作不會新開辟線程  

  1. 在串行隊列中執行同步任務:不會建立線程,按順序執行任務(毫無用處)
  2. 在串行隊列中執行異步任務,會建立線程,按順序執行任務(非常有用)
  1. 在并行隊列中執行同步任務:不會建立線程,按順序執行任務(幾乎沒用)
  2. 在并行隊列中執行異步任務:會建立多個線程,但是無法确定任務的執行順序(有用,但是很容易出錯)

  全局隊列:

  • 全局隊列是系統的,直接拿過來就可以用,與并行隊列類似,但是不能指定隊列的名字,調試時無法确認任務所在隊列
  • 在全局隊列中執行同步任務:不會建立線程,按順序執行任務
  • 在全局隊列中執行異步任務:會建立多個線程,但是無法确定任務的執行順序

主隊列:

  • 如果把任務放到主隊列中進行處理,那麼不論處理函數是異步的還是同步的都不會開啟新的線程。
  • 每一個應用程式隻有一個主線程即隻有一個主隊列
  • 為什麼需要再主線程上執行任務呢?因為在ios開發中,所有UI的更新任務都必須在主線程上執行。
  • 主隊列中的操作都是在主線程中執行的,不存在異步的概念
  • 在主線程中向 主隊列中添加的同步操作會死鎖

線程阻塞(延遲):三種方法

1 //過3秒後做一件事兒,這個做法會導緻主線程阻塞,不提倡使用
 2 - (IBAction)sleepDelay:(id)sender
 3 {
 4     NSLog(@"3秒後幹件事兒!");
 5     [NSThread sleepForTimeInterval:3];
 6     NSLog(@"幹事兒中...");
 7 }
 8 - (IBAction)performAfterDelay:(id)sender
 9 {
10     NSLog(@"3秒後幹件事兒!");
11     //在3秒後會啟動一個線程去完成任務(調用方法task),此方法并不會阻塞
12     [self performSelector:@selector(task) withObject:nil afterDelay:3];
13     NSLog(@"主線程繼續向下執行...");
14 }
15 - (void)task
16 {
17     NSLog(@"幹活中...");
18 }
19 //使用GCD實作延遲執行
20 - (IBAction)dispatchAfter:(id)sender
21 {
22     /*
23      1秒 = 1000毫秒
24      1毫秒 = 1000微秒
25      1微秒 = 1000納秒
26      */
27     //dispatch_time函數的第二個參數機關是納秒
28     NSLog(@"3秒鐘後做事");
29     dispatch_after(
30                    /**
31                     *  延遲的函數
32                     *
33                     *  @param DISPATCH_TIME_NOW 從現在起
34                     *  @param int64_t           延遲描述
35                     *    NSEC_PER_SEC 這個宏的意思是每秒多少納秒
36                     *  @return 無
37                     */
38         dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC),
39         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
40         ^{
41             NSLog(@"3秒後我開始做需要的事情啦!");
42     });
43 }      

View Code

線程間的通訊:

dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執⾏耗時的異步操作...
dispatch_async(dispatch_get_main_queue(), ^{

// 回到主線程,執⾏UI重新整理操作
});
});        

dispatch_once  參照:線程安全的單例  http://www.cnblogs.com/ChrisYu/p/4651114.html

小結:

       無論什麼隊列和什麼任務,線程的建立和回收不需要程式員參與,由隊列來負責,程式員隻需要面對隊列和任務。GCD在後端管理這一個線程池,GCD不僅決定着Block代碼塊将在哪個線程中被執行,而且還可以根據可用的系統資源對這些線程進行管理,進而讓開發者從線程管理的工作中解放出來,通過GCD這種集中的管理線程,緩解了大量的線程被建立的問題

4. NSOperation

單詞:

1. global   ge(0) lao(1) bao(3)  全局

2. concurrent   并發

3. queue  隊列

作業:

1. 從網上下載下傳圖檔并顯示到界面上。

要求,下載下傳過程中界面不能死。

用GCD做

2. 了解一個第三方架構SDWebImage

1 - (IBAction)serialOperation
 2 {
 3     //預設OperationQueue中的線程都是并行的
 4     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
 5     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
 6 //    [operation1 start];//不會啟動線程,直接在主線程中執行
 7     [queue addOperation:operation1];
 8     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
 9     //[operation2 start];
10     //線程二要依賴線程一,是以線程2會等線程一結束了再執行,按此方式可以讓兩個線程串行執行
11     [operation2 addDependency:operation1];
12     [queue addOperation:operation2];
13 }
14 
15 - (void)printPlusSignal
16 {
17     for (int i=0; i<10; i++) {
18         [NSThread sleepForTimeInterval:1];
19         NSLog(@"++++++++++++++");
20     }
21 }
22 
23 - (void)printMinusSignal
24 {
25     for (int i=0; i<10; i++) {
26         [NSThread sleepForTimeInterval:1];
27         NSLog(@"----------------");
28     }
29 }
30 
31 - (IBAction)concurrentQueue:(id)sender
32 {
33     //預設OperationQueue中的線程都是并行的
34     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
35     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
36    
37     [queue addOperation:operation1];
38     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
39 
40     [queue addOperation:operation2];
41 }
42 
43 //使用Block送出任務給OperationQueue
44 - (IBAction)blockQueue
45 {
46     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
47     //CallBack 回調
48     NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
49         for (int i=0; i<10; i++) {
50             [NSThread sleepForTimeInterval:1];
51             NSLog(@"++++++++++++++");
52         }
53     }];
54     [queue addOperation:operation1];
55     
56     NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
57         for (int i=0; i<10; i++) {
58             [NSThread sleepForTimeInterval:1];
59             NSLog(@"-------------");
60         }
61     }];
62     [queue addOperation:operation2];
63 }
64 - (IBAction)mainQueue
65 {
66     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
67     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
68         NSLog(@"在子線程中-----%@", [NSThread currentThread]);
69         //擷取主隊列(回到主線程)
70         NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
71         NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
72             NSLog(@"在主線程中?-----%@", [NSThread currentThread]);
73         }];
74         [mainQueue addOperation:operation2];
75     }];
76     [queue addOperation:operation];
77 }      

View Code

轉載于:https://www.cnblogs.com/ChrisYu/p/4649968.html