IOS GCD多執行緒

IOS GCD多執行緒

GCD
簡稱 Grand Central Dispatch ,利用在CPU(雙核,多核心的平行運算),自動管理Thread的Liftcycle ,(創造執行緒,調度Task,消除執行緒),不用自己管理Thread。


GCD
Objectivc-C 使用Block
Swift 使用Closure

Task & Queue

Task:

Operation ,一個程式區段,在GCD裡面就是Block或者Closure
有兩種操作模式:sync & async

差別在於是否創造 “新的Thread”

同步執行

同步操作會阻礙當前執行緒執行並等待,等當前執行緒完成才繼續執行

異步執行

異步操作,當前執行緒會繼續往下執行,不會阻礙執行緒

Queue

Serial Queues(串行序列)

會進行FIFO(First in First out)

Concurrent Queues(並行序列)

也是FIFO取出,但取出後會開啟多條執行緒直接取出又放到另外一個執行緒,由於取出動作很快,看起來所有任務同時執行,但是自根據系統資源控制並行數量,所以如果任務很多,不會同時讓任務一起執行。



Item sync async
Serial Queues 執行緒,一個一個執行 其他執行緒,一個一個執行
Concurrent Queues 執行緒,一個一個執行 很多執行緒,同時執行
創造Queue
Main Queue
Create Queue
Global Queue

Main Queue


mainqueue為自定義變數

  /*OBJECTIVE-C*/
  dispatch_queue_t mainqueue = dispatch_get_main_queue();

  /*SWIFT*/
  let mainqueue = dispatch_get_main_queue()


Create Your Own Queue

自己可以創建串行序列或者並行序列,
第一個參數”thisIsSerialQueues”是標示符號,用來Debug用,
第二個參數用來代表要創造何種序列


  //OBJECTIVE-C
  //Serial Queues
  dispatch_queue_t queue = dispatch_queue_create("thisIsSerialQueues", NULL);
  dispatch_queue_t queue = dispatch_queue_create("thisIsSerialQueues", DISPATCH_QUEUE_SERIAL);
  //Concurrent Queues
  dispatch_queue_t queue = dispatch_queue_create("thisIsConcurrentQueues", DISPATCH_QUEUE_CONCURRENT);

  //SWIFT
  //Serial Queues
  let queue = dispatch_queue_create("thisIsSerialQueues", nil);
  let queue = dispatch_queue_create("thisIsSerialQueues", DISPATCH_QUEUE_SERIAL)
  //Concurrent Queues
  let queue = dispatch_queue_create("thisIsConcurrentQueues", DISPATCH_QUEUE_CONCURRENT)
全域並行序列

這是系統提供的並行序列,只要是並行任務一般都夾到這個序列

 //OBJECTIVE-C
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  //SWIFT
  let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

創造Task

同步Task:會阻礙當前的執行緒
//OBJECTIVE-C
  dispatch_sync(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
//SWIFT
  dispatch_sync(<#queue#>, { () -> Void in
      //code here
      print(NSThread.currentThread())
  })


異步Task:不會阻礙當前的執行緒
OBJECTIVE-C
  dispatch_async(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
SWIFT
  dispatch_async(<#queue#>, { () -> Void in
      //code here
      print(NSThread.currentThread())
  })

範例一

let thread = NSThread.currentThread()
print("Before - \(thread)")

dispatch_sync(dispatch_get_main_queue(), { () -> Void in
    print("main - \(thread)")
})

print("After - \(thread)")

卡在

"Before - <NSThread: 0x7fc2ea402b70>{number = 1, name = main}\n"

這一行,同步序列會阻礙執行緒,將print("main - \(thread)") 這行任務放回主執行緒,因為dispatch_sync會立刻產生一個新的Queue,而這時卻使用主執行緒dispatch_get_main_queue()同步,因而造成死結。

同類案例

dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main, ^
                  {
                      [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationAuthenticationSuccess object:nil userInfo:ret];
                  });

當呼叫dispatch_sync的時候必須確保目前不在當前隊列dispatch_get_current_queue()。
因為dispatch_sync的特性是會等待Block完成後才繼續執行
所以如果當前序列dispatch_get_current_queue()與你等待排隊的Block正在相同的隊列上等待,例如主執行緒,那主執行緒會卡住等待並行率列,這時候將會造成死結。

解法:

//OBJECTIVE-C
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_block_t block = ^
              {
                  [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationAuthenticationSuccess object:nil userInfo:ret];
              };
if (dispatch_get_current_queue() == main)
  block(); // execute the block directly, as main is already the current active queue
else
  dispatch_sync(main, block); // ask the main queue, which is not the current queue, to execute the block, and wait for it to be executed before continuing

不過dispatch_get_current_queue已經被捨棄了
原因如下:

dispatch_get_current_queue never really made sense in the first place. Here’s why: There are a handful of “root” queues (one for each priority, and then the main queue). Every other queue ultimately ends up targeting one of these root queues. This means that, in the general case, there isn’t a single answer to the question, “What queue am I running on?”

dispatch_get_current_queue 這個方法並不符合邏輯,因為有根序列的序列屈指可數,只有幾個優先權序列和主執行緒,每個最終會結束在這些根序列,這代表著在一般的情況下,這不只有一個答案。

For instance, if you have queue B that targets queue A, then either A or B would be a reasonable answer to that question, for a block submitted to queue B. Furthermore, since all queues end up targeting one of the global/root queues, arguably the best answer would be “whatever root queue it ended up executing on”, except that’s not really useful to anyone, because it doesn’t significantly differentiate anything.

舉例說,如果你有一個序列B的針對序列A,A或B都有可能是合理的答案,當一個區段傳送到序列B,此外,所有的序列都會結束在一個全域序列,照理說,最好的答案會是“隨時根序列都會在執行中結束”,此外他不是對所有對象都有用,因為他無法顯著的區分所有對象。

In my experience, in most cases, what folks want from dispatch_get_current_queue is the answer to, “What queue was I initially submitted to?” However, by definition, whatever code submitted the block already knows what queue it was submitted to (because it’s doing the submission). So arguably, if you needed to capture that information, you could trivially do so at enqueue time; you don’t need dispatch_get_current_queue to answer that question. For these cases, dispatch_get_current_queue would just be a shortcut, and a flawed one at that (because of queue targeting.)
在我的經驗裡面,大部分的情況,人們需要dispatch_get_current_queue 是為了處理“ 我現在處理的序列是什麼”? 然而,對於定義來說,任何程式碼被提出在程式區段內,已經知道他被哪個序列提出(因為他是被動被處理),所以合理的情況,當你需要控制這項資訊,你可以很簡易的處理他在序列時間內,你不需要使用dispatch_get_current_queue 來解決這個問題。對於這些情況來說dispatch_get_current_queue 只是一個快捷鍵,一個有缺陷的快捷鍵,因為是針對queue設計的。

The other big class of cases is when you want to know if you’re on the main queue. -[NSThread isMainThread] is sufficient/authoritative for that, so you don’t need dispatch_get_current_queue for that either.
另外一個重要的案例是當你需要知道你是否正在主執行緒,[NSThread isMainThread] 已經足夠使用,所以你不需要dispatch_get_current_queue。

Another answerer also pointed out that dispatch_get_current_queue was frequently misused in an attempt to emulate recursive locking with GCD queues. It’s not possible to reliably implement recursive locks in a queue based system because “queues aren’t locks”. I’ve written at some length about that particular situation in another answer.
另外一種答案也指向經常被誤用在試圖模擬遞迴鎖用在GCD的序列上,他不能夠實現遞迴鎖在序列上,因為序列不能上鎖,我有另外一個答案在這一篇文章上。

原文網址:
http://stackoverflow.com/questions/23955948/why-did-apple-deprecate-dispatch-get-current-queue
延伸閱讀:
http://stackoverflow.com/questions/19494167/how-to-implement-a-reentrant-locking-mechanism-in-objective-c-through-gcd/19495517#19495517

範例二


let queue = dispatch_queue_create("thisIsSerialQueue", DISPATCH_QUEUE_SERIAL)

   NSLog("before - %@", NSThread.currentThread())

    dispatch_async(queue, { () -> Void in
        NSLog("syncBefore- %@", NSThread.currentThread())
        dispatch_sync(queue, { () -> Void in
        //卡住
             NSLog("sync - %@", NSThread.currentThread())
        })
        //被前面卡住
        NSLog("syncAfter - %@", NSThread.currentThread())
   })

  NSLog("After - %@", NSThread.currentThread())

1.
使用 DISPATCH_QUEUE_SERIAL ,建立了一個SerialQueue。
印出before - %@
2.
dispatch_async ,所以當前執行緒不會被阻塞,於是有兩條執行緒,一條繼續After - %@这句, 另一條執行 Block 中的內容 syncBefore- %@ 這句。因為這兩條同步進行,所以先後順序無關。
3.
現在的情況與上一個情況同樣。dispatch_sync同步執行,所以當前執行緒會被阻礙,一直等到 sync 裡的任務完成才會繼續。於是 sync 就把自己 Block 中的任務放到 queue 中,可是 queue 這時候是一個SerialQueue,一次執行一個任務,所以 sync 的 Block 必須等到前一個任務完成,可是没想到的是 queue “正在執行的任務就是被 sync 阻塞了的那個”。於是又發生了死結。所以 sync 所在的執行緒被卡死了。剩下的兩句程式碼不會被印出。


Serial Group

OBJECTIVE-C
//1.Create Serial Group
dispatch_group_t group = dispatch_group_create();

//2.Create queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//3.多次使用序列組的方法,使用異步
//3.1.執行三次循環
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 3; i++) {
        NSLog(@"group-01 - %@", [NSThread currentThread]);
    }
});

//3.2.主執行緒執行八次循環
dispatch_group_async(group, dispatch_get_main_queue(), ^{
    for (NSInteger i = 0; i < 8; i++) {
        NSLog(@"group-02 - %@", [NSThread currentThread]);
    }
});

//3.3.執行五次循環
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 5; i++) {
        NSLog(@"group-03 - %@", [NSThread currentThread]);
    }
});

//4.都完成後會自動通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"完成 - %@", [NSThread currentThread]);
});

SWIFT
//1.創建序列組
let group = dispatch_group_create()
//2.創建序列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

//3.多次使用序列組的方法,使用異步執行
//3.1.執行三次迴圈
dispatch_group_async(group, queue) { () -> Void in
    for _ in 0..<3 {
        NSLog("group-01 - %@", NSThread.currentThread())
    }
}

//3.2.主執行緒執行八次迴圈
dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in
    for _ in 0..<8 {
        NSLog("group-02 - %@", NSThread.currentThread())
    }
}

//3.3.執行五次迴圈
dispatch_group_async(group, queue) { () -> Void in
    for _ in 0..<5 {
        NSLog("group-03 - %@", NSThread.currentThread())
    }
}

//4.完成後通知
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
    NSLog("完成 - %@", NSThread.currentThread())
}

Comments

Popular posts from this blog

MEGA 暫存檔案刪除

利用CMD指令強制刪除