iOS中多線程的使用

來源:互聯網
上載者:User

標籤:主線程   串列   字元   標記   伺服器端   相互   bar   手動   set   

1.安全執行緒出現條件:多個線程訪問更改同一個變數

2.OC在定義屬性時有nonatomic和atomic兩種選擇

  • atomic:原子屬性,在setter方法中會為屬性加鎖(預設為atomic),安全執行緒,需要消耗大量的資源
  • nonatomic:非原子屬性,不會為setter方法加鎖,非安全執行緒,適合記憶體較小的行動裝置

iOS開發建議:

  • 所有屬性都聲明為nonatomic
  • 盡量避免多條線程搶奪同一塊資源
  • 精良將加鎖、資源搶奪的商務邏輯交給伺服器端處理,減小移動用戶端的壓力
GCD(Grand Central Dispatch 中樞調度器)
  • GCD是蘋果公司為多核的並行運算提出的解決方案
  • GCD會自動利用更多的CPU核心
  • GCD會自動管理線程的聲明周期
  • 程式員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼
任務和隊列

GCD的兩個核心

  • 任務:執行什麼操作
  • 隊列:存放任務

將任務添加到隊列

  • GCD會自動將隊列中的任務取出,放到對應的線程中執行
  • 熱舞的取出遵循隊列的FIFO原則: 先進先出,後進後出

同步和非同步區別

  • 同步:只能在當前線程中執行,不具備開啟新線程的能力
  • 非同步:可以在新的線程中執行任務,具備開啟新縣城的能力(並非一定開啟新線程)

隊列的類型

  • 並發隊列:
    • 可以讓多個任務並發(同時)執行(自動開啟多個線程同時執行任務)
    • 並發功能只有在非同步(dispatch_async)函數下才有效非同步(dispatch_async)函數下才有效
  • 串列隊列:
    • 讓任務一個接一個地執行(一個任務執行完畢後再執行下一個任務)
  • 隊列的建立
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue",           //傳遞的字串參數來標記線程DISPATCH_QUEUE_CONCURRENT);  //參數包括DISPATCH_QUEUE_SERIAL (串列隊列)                                        DISPATCH_QUEUE_CONCURRENT (並行隊列)

小結:

  • 同步和非同步主要影響:任務的執行方式
  • 並發:允許多個任務並發(同時)執行
  • 串列:一個任務執行完畢後,再執行下一個任務

並發隊列

  • GCD預設已經提供了全域的並發隊列,供整個應用使用,可以無需手動建立
    • 使用dispatch_get_global_queue函數獲得全域的並發隊列
    dispatch_get_global_queue(dispatch_queue_priority_t priority,     //隊列的優先順序unsigned long flags);     //此參數暫時無用,用0即可

串列隊列

  • GCD中獲得串列有兩種途徑
    • 使用dispatch_queue_create函數建立串列隊列
      //建立串列隊列(隊列類型傳遞NULL或者DISPATCH_ QUEUE_SERIAL)
  • 主隊列的使用
    • 主隊列是GCD內建的一種特殊的串列隊列
    • 放在主隊列中的任務都會在主線程中執行
    • 使用dispatch_ get_ main_ queue()獲得主隊列
    • 非同步函數正常情況下會開線程,但當非同步函數和主線程結合時非同步函數並不會開啟新的線程,eg:在下面的代碼執行中輸出為{number = 1, name = main},由此可見當主線程與非同步函數結合時並沒有開啟新的線程,並且執行方式為串列執行
dispatch_queue_t mainQueue = dispatch_get_main_queue();    dispatch_async(mainQueue, ^{        NSLog(@"1----%@", [NSThread currentThread]);    });    dispatch_async(mainQueue, ^{        NSLog(@"2----%@", [NSThread currentThread]);    });    dispatch_async(mainQueue, ^{        NSLog(@"3----%@", [NSThread currentThread]);    });    dispatch_async(mainQueue, ^{        NSLog(@"4----%@", [NSThread currentThread]);    });    dispatch_async(mainQueue, ^{        NSLog(@"5----%@", [NSThread currentThread]);    });
  • 線程死結
    • 線程死結條件
      • 調用方法的所在的隊列與新增工作的隊列為同一隊列
      • 添加的任務為同步執行函數(同步執行即當前任務未執行完不執行下一個任務)
    • 原因:代碼如下,我們線上程中調用lock方法時線程會將該方法添加到隊列中,當我們在同一線程調用同步函數新增工作時該函數會等待隊列中的lock方法執行完畢再新增工作,但是添加同步執行函數的方法又存在於lock方法內部,不新增工作該方法就無法完成,從而形成無法新增工作並且該方法也無法執行結束的局面,形成線程死結。在主線程中調用下面的方法就會引起線程死結
- (void)lock{    dispatch_queue_t mainQueue = dispatch_get_main_queue();    NSLog(@"開啟任務");    dispatch_sync(mainQueue, ^{        NSLog(@"1----%@", [NSThread currentThread]);    });    dispatch_sync(mainQueue, ^{        NSLog(@"2----%@", [NSThread currentThread]);    });    dispatch_sync(mainQueue, ^{        NSLog(@"3----%@", [NSThread currentThread]);    });    dispatch_sync(mainQueue, ^{        NSLog(@"4----%@", [NSThread currentThread]);    });    dispatch_sync(mainQueue, ^{        NSLog(@"5----%@", [NSThread currentThread]);    });}

註:線程死結鎖的到底是什麼

分析在主線程調用下面代碼是否會產生死結

- (void)lock{    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);    NSLog(@"當前線程----%@", [NSThread currentThread]);    dispatch_sync(serialQueue, ^{        NSLog(@"1----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"2----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"3----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"4----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"5----%@", [NSThread currentThread]);    });    NSLog(@"end");}

在上面的代碼調用方法在主線程,而且代碼為同步執行,運行輸出結果也在主線程,但在實際的運行中並沒有出現死結的現象,這是因為代碼中lock所在的隊列和lock中添加的任務隊列並不是同一個隊列,所以並不會出現任務相互等待的現象。換句話說上面描述的線程死結是因為同一隊列中任務的相互等待而造成的。

線程運行結果

  • 同步串列/非同步串列執行的線程:在如下代碼中,添加到線程中的任務會依次執行
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);    NSLog(@"1.當前線程----%@", [NSThread currentThread]);    dispatch_sync(serialQueue, ^{        NSLog(@"2----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"3----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"4----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"5----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"6----%@", [NSThread currentThread]);    });    NSLog(@"end");

因為均為同步函數,輸出依次執行, 根據非同步執行的特點不難分析出非同步執行的結果為1、end、2、3、4、5;

  • 非同步串列摻雜同步串列
 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);    NSLog(@"1.當前線程----%@", [NSThread currentThread]);    dispatch_async(serialQueue, ^{        NSLog(@"2----%@", [NSThread currentThread]);    });    dispatch_async(serialQueue, ^{        NSLog(@"3----%@", [NSThread currentThread]);    });    dispatch_sync(serialQueue, ^{        NSLog(@"4----%@", [NSThread currentThread]);    });    dispatch_async(serialQueue, ^{        NSLog(@"5----%@", [NSThread currentThread]);    });    dispatch_async(serialQueue, ^{        NSLog(@"6----%@", [NSThread currentThread]);    });    NSLog(@"end");    

在上面的代碼中2、3、5、6任務為非同步執行,4的任務為同步執行,輸出結果為1、2、3、4、end、5、6;並且其執行的線程也會發生變化。這是由於在方法執行中首先將2、3、4函數添加到隊列中,但是4方法為同步執行方法所以其會等待隊列中的任務執行完畢後再往下執行,並且線程的同步執行任務並不會開啟新的線程,所以4方法回到主線程中執行,2、3、5、6為非同步執行,與4方法不在同一線程。5、6為非同步執行,添加到隊列後並不會立即執行,所以輸出在end之後。

柵欄函數

  • 在非同步並行多任務執行的隊列中,當我們希望前面的任務完成後再執行後面的任務時可使用柵欄函數來實現這個功能。
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);        dispatch_async(queue, ^{        NSLog(@"1--- %@", [NSThread currentThread]);    });    dispatch_async(queue, ^{        NSLog(@"2--- %@", [NSThread currentThread]);    });        dispatch_async(queue, ^{        NSLog(@"3--- %@", [NSThread currentThread]);    });        //柵欄函數    dispatch_barrier_async(queue, ^{        NSLog(@"------上面的已經執行完畢------");    });        dispatch_async(queue, ^{        NSLog(@"4--- %@", [NSThread currentThread]);    });

註:柵欄函數不適用於全域並發隊列(dispatch_ globle_queue)

dispatch_ async_ f的使用

dispatch_ async_ f和dispatch_ async功能相同,只是調用方式不同,dispatch_ async_f的使用如下

dispatch_queue_t globleQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_async_f(globleQueue, NULL, tast);    void tast(void *param){    NSLog(@"%s --- %@", __func__, [NSThread currentThread]); }

iOS中多線程的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.