iOS 多線程 01

  • 進程安全

    • 進程是指在系統中正在運行的一個應用程序
  • 線程多線程

    • 1個進程要想執行任務,必須得有線程(每1個進程至少要有1條線程)
    • 1個線程中任務的執行是串行的(執行完上一個才能執行下一個)
  • 多線程併發

    • 1個進程中能夠開啓多條線程,多條線程能夠並行(同時)執行不一樣的任務
    • 線程能夠並行, 可是每一個線程中的任務仍是串行
  • 多線程原理app

    • 多線程併發(同時)執行,實際上是CPU快速地在多條線程之間調度(切換)
  • 多線程優缺點異步

    • 優勢
      • 能適當提升程序的執行效率
      • 能適當提升資源利用率(CPU、內存利用率)
    • 缺點
      • 線程越多,CPU在調度線程上的開銷就越大
      • 若是開啓大量的線程,會下降程序的性能
      • 程序設計更加複雜:好比線程之間的通訊、多線程的數據共享

  • pthread
    • 類型: C語言中類型的結尾一般 _t/Ref,並且不須要使用 *
    • /*
      參數:
      1. 線程代號的地址
      2. 線程的屬性
      3. 調用函數的指針
        - void *(*)(void *)
        - 返回值 (函數指針)(參數)
        - void * 和 OC 中的 id 是等價的
      4. 傳遞給該函數的參數
      返回值:
      若是是0,表示正確
      若是是非0,表示錯誤碼
      */
      NSString *str = @"jx";
      pthread_t thid;
      int res = pthread_create(&thid, NULL, &demo, (__bridge void *)(str));
      if (res == 0) {
        NSLog(@"OK");
      } else {
        NSLog(@"error %d", res);
      }
  • NSThreadasync

    • 一個NSThread對象就表明一條線程
  • 建立線程的幾種方式函數

  • // 1.建立線程
     NJThread *thread = [[NJThread alloc] initWithTarget:self selector:@selector(demo:) object:@"jx"];
     // 設置線程名稱 [thread setName:@"ljx"]; 
    // 設置線程的優先級 
    // 優先級僅僅說明被CPU調用的可能性更大 
      [thread setThreadPriority:1.0];
     // 2.啓動線程 [thread start];
    - detach/performSelector
        + 優勢:簡單快捷
        + 缺點:沒法對線程進行更詳細的設置
    
    ```objc
    // 1.建立線程
    [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"jx"];
    
    // 1.建立線程
    // 注意: Swift中不能使用, 蘋果認爲這個方法不安全
        [self performSelectorInBackground:@selector(demo:) withObject:@"jx"];
     
      
  • 線程狀態
  •     啓動線程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread start];
    
        // 進入就緒狀態 ->         運行狀態。當線程任務執行完畢,自動進入死亡狀態
    
        阻塞(暫停)線程
        + (void)sleepUntilDate:(NSDate *)date;
        + (void)sleepForTimeInterval:(NSTimeInterval)ti;
        // 進入阻塞狀態
    
        強制中止線程
        + (void)exit;
        // 進入死亡狀態
    
    注意:一旦線程中止(死亡)了,就不能再次開啓任務
    
       如圖:

    多線程的安全隱患性能

    • 被鎖定的代碼同一時刻只能有一個線程執行
    • @synchronized(鎖對象) { // 須要鎖定的代碼  }
  • 互斥鎖的優缺點 優勢:能有效防止因多線程搶奪資源形成的數據安全問題 缺點:須要消耗大量的CPU資源atom

  • 互斥鎖注意點url

    • 鎖定1份代碼只用1把鎖,用多把鎖是無效的
    • 鎖定範圍越大, 性能越差

  • 原子和非原子屬性

    • atomic:線程安全,須要消耗大量的資源
    • nonatomic:非線程安全,適合內存小的移動設備
  • 自旋鎖 & 互斥鎖

    • 共同點 都可以保證同一時間,只有一條線程執行鎖定範圍的代碼
    • 不一樣點
      • 互斥鎖:若是發現有其餘線程正在執行鎖定的代碼,線程會進入"休眠"狀態,等待其餘線程執行完畢,打開鎖以後,線程會被"喚醒"
      • 自旋鎖:若是發現有其餘線程正在執行鎖定的代碼,線程會"一直等待"鎖定代碼執行完成! 自旋鎖更適合執行很是短的代碼!

  • 線程間通訊
      • 子線程作耗時操做, 主線程更新數據
  • #import "ViewController.h"
    
    @interface ViewController ()
    
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    @end
    
    @implementation ViewController
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        
        // 開啓一個子線程下載圖片
        [self performSelectorInBackground:@selector(downlod) withObject:nil];
    }
    
    - (void)downlod
    {
        NSLog(@"%@", [NSThread currentThread]);
        // 1.下載圖片
         NSURL *url = [NSURL URLWithString:@"http://pic.4j4j.cn/upload/pic/20130531/07ed5ea485.jpg"];
         NSData *data = [NSData dataWithContentsOfURL:url];
        // 2.將二進制轉換爲圖片
        UIImage *image = [UIImage imageWithData:data];
        
        // 3.跟新UI
    #warning 注意: 千萬不要在子線程中更新UI, 會出問題
        
        /*
         waitUntilDone: 
         YES: 若是傳入YES, 那麼會等待updateImage方法執行完畢, 纔會繼續執行後面的代碼
         NO:  若是傳入NO, 那麼不會等待updateImage方法執行完畢, 就能夠繼續以後後面的代碼
         */
        /*
        [self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:NO];
         */
        
        // 開發中經常使用
    //    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
        
        // 能夠在指定的線程中, 調用指定對象的指定方法
        [self performSelector:@selector(updateImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
        
    }
    
    
    - (void)updateImage:(UIImage *)image
    {
        NSLog(@"%@", [NSThread currentThread]);
        // 3.更新UI
        self.imageView.image = image;
        
    
    }
  • GCD
  • GCD中有2個核心概念

    • 任務:執行什麼操做
    • 隊列:用來存聽任務
  • 執行任務

    • 同步方法: dispatch_sync
    • 異步方法: dispatch_async
    • 同步和異步的區別
      • 同步:只能在當前線程中執行任務,不具有開啓新線程的能力
      • 異步:能夠在新的線程中執行任務,具有開啓新線程的能力
  • 隊列

    • 併發隊列
      • 可讓多個任務併發(同時)執行(自動開啓多個線程同時執行任務)
      • 併發功能只有在異步(dispatch_async)函數下才有效
      • GCD默認已經提供了全局的併發隊列,供整個應用使用,能夠無需手動建立
                    使用dispatch_get_global_queue函數得到全局的併發隊列
                    dispatch_queue_t dispatch_get_global_queue(
                    dispatch_queue_priority_t priority, //                    隊列的優先級
                    unsigned long flags); // 此參數暫時無用,用0便可
        
                    得到全局併發隊列
                    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
                    全局併發隊列的優先級
                    #define DISPATCH_QUEUE_PRIORITY_HIGH 2 //
                    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中)
                    #define DISPATCH_QUEUE_PRIORITY_LOW (-2) //
                    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 後臺

         

    • 串行隊列
          * 讓任務一個接着一個地執行(一個任務執行完畢後,再執行下一個任務)
    •         GCD中得到串行有2種途徑
              使用dispatch_queue_create函數建立串行隊列
              // 建立串行隊列(隊列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL)
              dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
      
              使用主隊列(跟主線程相關聯的隊列)
              主隊列是GCD自帶的一種特殊的串行隊列
              放在主隊列中的任務,都會放到主線程中執行
              使用dispatch_get_main_queue()得到主隊列
              dispatch_queue_t queue = dispatch_get_main_queue();
    • 注意點

      同步和異步主要影響:能不能開啓新的線程
      • 同步:只是在當前線程中執行任務,不具有開啓新線程的能力
      • 異步:能夠在新的線程中執行任務,具有開啓新線程的能力
      併發和串行主要影響:任務的執行方式
      • 併發:容許多個任務併發(同時)執行
      • 串行:一個任務執行完畢後,再執行下一個任務
    • 各類任務隊列搭配

      • 同步 + 串行 *
      • 同步 + 併發 *
      • 異步 + 串行 *
      • 異步 + 併發 *
      • 異步 + 主隊列 *
      • 同步 + 主隊列 *
    • GCD線程間通訊

    • dispatch_async(
      dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          // 執行耗時的異步操做...
            dispatch_async(dispatch_get_main_queue(), ^{
              // 回到主線程,執行UI刷新操做
              });
      });
    • GCD其它用法
    • 延時執行
    • dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
          // 2秒後執行這裏的代碼...
      });

       

    • 一次性代碼

      • 使用dispatch_once函數能保證某段代碼在程序運行過程當中只被執行1次
    • static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 只執行1次的代碼(這裏面默認是線程安全的) });

       

    • 快速迭代
    • dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
          // 執行10次代碼,index順序不肯定
      });
    • barrier
      • 在前面的任務執行結束後它才執行,並且它後面的任務等它執行完成以後纔會執行
      • 不能是全局的併發隊列
      • 全部的任務都必須在一個隊列中
      • dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    • 隊列組
    • dispatch_group_t group =  dispatch_group_create();
      dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          // 執行1個耗時的異步操做
      });
      dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          // 執行1個耗時的異步操做
      });
      dispatch_group_notify(group, dispatch_get_main_queue(), ^{
          // 等前面的異步操做都執行完畢後,回到主線程...
      });
    • iOS中多線程的實現方案

      • 如圖:
    • 單例模式

      • 單例模式的做用

        - 能夠保證在程序運行過程,一個類只有一個實例,並且該實例易於供外界訪問,從而方便地控制了實例個數,並節約系統資源
      • 單例模式的使用場合

        • 在整個應用程序中,共享一份資源(這份資源只須要建立初始化1次)
      • ARC中,單例模式的實現

      • 在.m中保留一個全局的static的實例
                static id _instance;
        
                重寫allocWithZone:方法,在這裏建立惟一的實例(注意線程安全)
                + (instancetype)allocWithZone:(struct _NSZone *)zone
                {
        
                    static dispatch_once_t onceToken;
                    dispatch_once(&onceToken,^{
                    _instance = [super allocWithZone:zone];
                    });
                     return _instance;
                }
        
                提供1個類方法讓外界訪問惟一的實例
                + (instancetype)sharedInstance
                {
                    static dispatch_once_t onceToken;
                    dispatch_once(&onceToken, ^{
                    _instance = [[self alloc] init];
                     });
                    return _instance;
                }
        
                實現copyWithZone:方法
                - (id)copyWithZone:(struct _NSZone *)zone
                {
                    return _instance;
                }
                注意點
                    // 注意點: 單例是不能夠繼承的, 若是繼承引起問題
                    // 若是先建立父類, 那麼永遠都是父類
                    // 若是先建立子類, 那麼永遠都是子類
相關文章
相關標籤/搜索