iOS中的多線程

Nsthread  

須要手動去管理線程的生命週期    可是他能夠方便咱們知道當前線程是什麼類的線程 可讓咱們知道當前線程的各類屬性 服務器

建立並啓動多線程

先建立 再啓動併發

// 建立
  NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

  // 啓動
  [thread start];

建立並自動啓動異步

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

除了建立啓動以外 NSThread還有不少方法 async

//取消線程
- (void)cancel;

//啓動線程
- (void)start;

//判斷某個線程的狀態的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;

//設置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;

//獲取當前線程信息
+ (NSThread *)currentThread;

//獲取主線程信息
+ (NSThread *)mainThread;

//使當前線程暫停一段時間,或者暫停到某個時刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;

GCD

充分的利用了CPU  自動管理線程的生命週期  不須要咱們去完成  咱們只須要告訴他咱們想要作什麼就行spa

GCD中有兩個很是重要的概念 : 任務 和 隊列線程

任務:  就是說明你想要作什麼  說白了就是一段代碼  在GCD中就是一段blockcode

任務有兩種執行方式   同步執行 和異步執行orm

同步執行: 他會阻塞當前線程 等待Block中的任務執行完畢 而後當前線程纔會繼續往下運行.對象

異步執行:當前線程會直接往下執行,它不會阻塞當前線程

隊列:用於存聽任務。一共有兩種隊列  串行隊列和並行隊列

串行隊列: 放到串行隊列的任務 會取出來一個 執行一個 而後取下一個 這樣一個一個的執行。

並行隊列:取出來一個會放到別的線程,而後再取取出來再放到另外一個線程 這樣因爲取的動做很快 忽略不計 看起來全部的任務都是一塊兒執行的

同步串行  : 當前線程  一個一個執行

同步並行  : 當前線程  一個一個執行

異步串行  : 其餘線程 一個一個執行

異步並行  : 開不少線程 一塊兒執行

建立隊列

主隊列: 特殊的串行隊列  用於刷新UI  任何須要刷新UI的工做都要在祝隊列執行  因此通常耗時的任務都放在別的線程執行

本身建立的隊列 : 串行隊列和並行隊列 有兩個參數 第一個參數是一個標識符  第二個參數用來表示是串行的仍是並行的

//串行隊列
  dispatch_queue_t queue = dispatch_queue_create("my Queue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_SERIAL);
  //並行隊列
  dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_CONCURRENT);

全局並行隊列:只要是並行任務通常都加入到這個隊列 這是系統提供的一個併發隊列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

建立任務

同步任務 : 會阻塞當前線程

dispatch_sync(<#queue#>, ^{
      
      NSLog(@"%@", [NSThread currentThread]);
  });

異步任務:不會阻塞當前線程

dispatch_async(<#queue#>, ^{
      
      NSLog(@"%@", [NSThread currentThread]);
  });

爲了更好的理解 看兩個示例

如下代碼在主線程調用  會是什麼結果

NSLog(@"以前 - %@", [NSThread currentThread]);
    
    dispatch_sync(dispatch_get_main_queue(), ^{
       
        NSLog(@"sync - %@", [NSThread currentThread]);
    
    });
    
    NSLog(@"以後 - %@", [NSThread currentThread]);

答案:
只會打印第一句: 以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main},而後主線程就卡死了,你能夠在界面上放一個按鈕,你就會發現點不了了。
解釋:
同步任務會阻塞當前線程,而後把 Block 中的任務放到指定的隊列中執行,只有等到 Block 中的任務完成後纔會讓當前線程繼續往下運行。
那麼這裏的步驟就是:打印完第一句後 dispath_sync 當即阻塞當前的主線程,而後把 Block 中的任務放到 main_queue 中,但是 main_queue 中的任務會被取出來放到主線程中執行,但主線程這個時候已經被阻塞了,因此 Block 中的任務就不能完成,它不完成 dispath_sync 就會一直阻塞主線程,這就是死鎖現象。致使主線程一直卡死。

以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main}

如下代碼會產生什麼結果

dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"以前 - %@", [NSThread currentThread]);
    
    dispatch_async(queue, ^{
        
        NSLog(@"sync以前 - %@", [NSThread currentThread]);
        
        dispatch_sync(queue, ^{
            
            NSLog(@"sync - %@", [NSThread currentThread]);
            
        });
        
        NSLog(@"sync以後 - %@", [NSThread currentThread]);
        
    });
    NSLog(@"以後 - %@", [NSThread currentThread]);

答案:

2016-12-26 13:25:31.689 ppp[41807:1430208] 以前 - <NSThread: 0x60800006ccc0>{number = 1, name = main}

2016-12-26 13:25:31.690 ppp[41807:1430208] 以後 - <NSThread: 0x60800006ccc0>{number = 1, name = main}

2016-12-26 13:25:31.690 ppp[41807:1430277] sync以前 - <NSThread: 0x608000261280>{number = 4, name = (null)}

很明顯 sync - %@ 和sync以後 - %@ 沒有被打印出來 崩潰了

分析:

咱們按執行順序一步一步來:

1.使用 DISPATCH_QUEUE_SERIAL 這個參數,建立了一個串行隊列

2.打印出 以前 - %@ 這句

3.dispatch_async 異步執行 因此當前線程不會被阻塞  就有了兩條線程 一條當前線程繼續往下打印出 以後 - %@ 這句 另外一條執行Block中的內容 打印 sync以前 - %@這句 由於這兩條是並行的 因此打印前後順序無所謂

4.如今的狀況和上一個例子同樣了 dispath_sync 同步執行  因而它所在的線程會被阻塞  一直等到 sync 裏的任務執行完纔會繼續往下  因而 sync 就高興的把本身 Block 中的任務放到 queue 中  但是 queue 是一個串行隊列  一次執行一個任務  因此 sync 的 Block 必須等到前一個任務執行完畢  可沒想到的是 queue 正在執行的任務就是被 sync 阻塞了的那個 因而又發生了死鎖 因此 sync 所在的線程被卡死了 剩下的兩句代碼天然不會打印 

NSOperation

NSOperation 是對GCD的封裝 徹底面向對象  NSOperation和NSOperationQueue分別對應GCD的任務和隊列.

1.將要執行的任務封裝到一個NSOperation對象中

2.將此任務添加到一個NSOperationQueue對象中

系統會自動執行任務

添加任務

NSOperation是一個抽象類  不能封裝任務  能夠用他的兩個子類用於封裝任務  分別是: NSInvocationOperation和NSBlockOperation 建立一個Operation後 須要用start方法來調用 它會默認在當前隊列同步執行  也能夠在中途取消一個任務  調用cancel方法.

NSInvocationOperation : 須要傳入一個方法名。

//1.建立NSInvocationOperation對象
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

  //2.開始執行
  [operation start];

NSBlockOperation

//1.建立NSBlockOperation對象
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@", [NSThread currentThread]);
  }];

  //2.開始任務
  [operation start];

以前說過這樣的任務,默認會在當前線程執行。可是NSBlockOperation還有一個方法: addExecutionBlock:,經過這個方法能夠給 Operation 添加多個執行 Block。這樣 Operation 中的任務 會併發執行,它會 在主線程和其它的多個線程 執行這些任務,注意下面的打印結果:

//1.建立NSBlockOperation對象
      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
          NSLog(@"%@", [NSThread currentThread]);
      }];

      //添加多個Block
      for (NSInteger i = 0; i < 5; i++) {
          [operation addExecutionBlock:^{
              NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
          }];
      }

      //2.開始任務
      [operation start];

打印輸出

2016-12-26 13:43:04.015 ppp[42039:1438406] <NSThread: 0x60800006f540>{number = 1, name = main}

2016-12-26 13:43:04.015 ppp[42039:1438524] 2次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}

2016-12-26 13:43:04.015 ppp[42039:1438522] 0次:<NSThread: 0x6000002622c0>{number = 3, name = (null)}

2016-12-26 13:43:04.015 ppp[42039:1438521] 1次:<NSThread: 0x60000026ba80>{number = 4, name = (null)}

2016-12-26 13:43:04.016 ppp[42039:1438406] 3次:<NSThread: 0x60800006f540>{number = 1, name = main}

2016-12-26 13:43:04.016 ppp[42039:1438524] 4次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}

NOTE: addExecutionBlock:方法必須在 start() 方法以前執行,不然就會報錯:

‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'

建立隊列

主隊列

NSOperationQueue *queue = [NSOperationQueue mainQueue];

其餘隊列

//1.建立一個其餘隊列    
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//2.建立NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];

//3.添加多個Block
for (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
    }];
}

//4.隊列添加任務
[queue addOperation:operation];

打印輸出

2016-12-26 13:48:55.611 ppp[42100:1441056] 1次:<NSThread: 0x608000462240>{number = 6, name = (null)}

2016-12-26 13:48:55.611 ppp[42100:1441055] <NSThread: 0x600000261800>{number = 3, name = (null)}

2016-12-26 13:48:55.611 ppp[42100:1441067] 2次:<NSThread: 0x60800027a740>{number = 4, name = (null)}

2016-12-26 13:48:55.611 ppp[42100:1441058] 0次:<NSThread: 0x608000462180>{number = 5, name = (null)}

2016-12-26 13:48:55.612 ppp[42100:1441056] 3次:<NSThread: 0x608000462240>{number = 6, name = (null)}

2016-12-26 13:48:55.612 ppp[42100:1441055] 4次:<NSThread: 0x600000261800>{number = 3, name = (null)}

 

沒有串行隊列?

NSOperationQueue有一個參數 maxConcurrentOperationCount 最大併發數,用來設置最多可讓多少個任務同時執行。當你把它設置爲 1 的時候,他就是串行!

NSOperationQueue還有一個添加任務的方法- (void)addOperationWithBlock:(void (^)(void))block; 這樣能夠添加一個任務到隊列中  十分方便

 

NSOperation有一個很是實用的功能 那就是添加依賴 好比有 3 個任務  A: 從服務器上下載一張圖片  B:給這張圖片加個水印   C:把圖片返回給服務器。這時就能夠用到依賴了.

//1.任務一:下載圖片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下載圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//2.任務二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"打水印   - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//3.任務三:上傳圖片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

//4.設置依賴
[operation2 addDependency:operation1];      //任務二依賴任務一
[operation3 addDependency:operation2];      //任務三依賴任務二

//5.建立隊列並加入任務
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

打印結果

2016-12-26 13:54:33.953 ppp[42218:1444656] 下載圖片 - <NSThread: 0x60800007ac00>{number = 3, name = (null)}

2016-12-26 13:54:35.028 ppp[42218:1444659] 打水印   - <NSThread: 0x60800007f280>{number = 4, name = (null)}

2016-12-26 13:54:36.103 ppp[42218:1444659] 上傳圖片 - <NSThread: 0x60800007f280>{number = 4, name = (null)}

注意: 不能添加相互依賴 會死鎖 好比A依賴B B依賴A

可使用 removeDependency 來解除依賴關係

能夠在不一樣的隊列之間依賴  反正就是這個依賴是添加到任務身上的  和隊列不要緊

還有一些方法

NSOperation

BOOL executing; //判斷任務是否正在執行

BOOL finished; //判斷任務是否完成

void (^completionBlock)(void); //用來設置完成後須要執行的操做

- (void)cancel; //取消任務

- (void)waitUntilFinished; //阻塞當前線程直到此任務執行完畢

NSOperationQueue

NSUInteger operationCount; //獲取隊列的任務數

- (void)cancelAllOperations; //取消隊列中全部的任務

- (void)waitUntilAllOperationsAreFinished; //阻塞當前線程直到此隊列中的全部任務執行完畢

[queue setSuspended:YES]; // 暫停queue

[queue setSuspended:NO]; // 繼續queue

 

從其餘線程回到主線程的方法

NSThread

[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];

GCD

dispatch_async(dispatch_get_main_queue(), ^{

});

NSOperationQueue

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];

好啦  就這麼多啦  可能寫的也不是特別的好 多線程也不止這麼些東西  謝謝你們

相關文章
相關標籤/搜索