2016-10-11 62 views
3

我正在嘗試下面的代碼在操場上,但它似乎沒有按照我的預期工作。如何在Swift中處理組等待結果3

兩個group_async操作總共導致我的Mac上約5秒到6秒。

  • 當我將超時時間設置爲DispatchTime.now()+ 10時,會同時打印「測試返回」和「完成」。
  • 當我將超時時間設置爲DispatchTime.now()+ 1(某些值使組超時)時,除了兩個group_async操作中的打印代碼外,不會打印任何內容。

我想要的是暫停組並在超時時做一些清理工作,並在組成功完成時進行其他一些操作。任何建議表示讚賞。謝謝。

import Dispatch 
import PlaygroundSupport 

PlaygroundPage.current.needsIndefiniteExecution = true 
let queue = DispatchQueue.global(qos: .utility) 

func test() { 
    let group = DispatchGroup() 
    __dispatch_group_async(group, queue) { 
     var a = [String]() 
     for i in 1...999 { 
      a.append(String(i)) 
      print("appending array a...") 
     } 
     print("a finished") 
    } 

    __dispatch_group_async(group, queue) { 
     var b = [String]() 
     for i in 1...999 { 
      b.append(String(i)) 
      print("appending array b...") 
     } 
     print("b finished") 
    } 

    let result = group.wait(timeout: DispatchTime.now() + 10) 
    if result == .timedOut { 
     group.suspend() 
     print("timed out") 
    } 
    print("test returns") 
} 


queue.async { 
    test() 
    print("done") 
} 
+0

@Rob我只試過這種在操場上。你的意思是在這兩種情況下行爲會有所不同嗎? – Evan

+0

謝謝你的建議。我會稍後在我的應用程序中嘗試。 – Evan

+0

嘿,@Rob。我已經在一個新的應用程序中測試過了。超時代碼被執行。現在,我會小心使用操場來測試由於不一致導致的GCD行爲。 – Evan

回答

2

此代碼段提出了各種不同的問題:

  1. 我注意到,行爲不同操場之間的一點,當你在一個應用程序中運行它。我懷疑這是needsIndefiniteExecutionPlaygroundPage和GCD的一些特質。我建議在一個應用中測試這個。有了我在下面提出的要點的警告,當我從應用程序運行此應用程序時,它會按預期工作。

  2. 我注意到你已經採用了這樣的模式:

    __dispatch_group_async(group, queue) { 
        ... 
    } 
    

    我建議:

    queue.async(group: group) { 
        ... 
    } 
    
  3. 你正在做group.suspend()。幾個注意事項:

    • 一個掛起隊列,而不是組。

    • 如果您曾打電話suspend(),請確保您有相應的電話resume()某處。

    • 此外,請記住,suspend()停止啓動未來的塊,但它不會對可能已在運行的塊執行任何操作。如果你想停止已經運行的塊,你可能想要取消它們。

    • 最後,請注意,您只能暫停您創建的隊列和來源。你不能(也不應該)暫停一個全局隊列。

  4. 我也注意到,你在你派出test()調用同一個隊列使用wait。在這種情況下,由於它是一個併發隊列,所以你會越來越遠,但這種模式會導致死鎖。如果可以的話,我建議完全避免wait,並且肯定不要在你調用它的同一個隊列上進行。再一次,這裏不是問題,但是這種模式可能會在未來讓你陷入困境。

    就個人而言,我可能傾向於使用notify而不是wait觸發代碼塊在兩個調度塊完成時運行。這消除了任何死鎖風險。如果我想要一段代碼在一段時間後運行(即超時過程),我可能會使用計時器觸發某些清理過程,以防這兩個塊仍在運行(可能取消它們;請參閱How to stop a DispatchWorkItem in GCD? )。

+0

請求超時?我認爲這是使用組和等待(超時)這種情況的一個很好的理由。否則,使用用戶定義的併發隊列並等待.barrier鎖定所有要完成的工作是一個更好的和「堅實」的想法。 – user3441734

+0

我恭敬地不同意。我的最後一段概述了我的看法:更好的方法是創建可取消的任務/操作,並讓定時器取消任何在一段時間後都沒有完成的任務。 「等待超時」是弱模式,恕我直言(阻止額外的線程;忽略基本的清理任務;引入死鎖風險等)。嘿,如果你想「等待」,請隨意,但建議最好是有爭議的。 – Rob

+0

asyncAfter方法意思是「超時後停止所有工作」,但如果所有其他工作已經完成,怎麼不運行它?這正是調度組是libdispatch一部分的場景。我同意,在組上使用等待或通知必須正確完成。埃文所做的最糟糕的事情就是直接使用全球隊列。創建自己的隊列意味着將控件放在你的手中:-)如果有人需要同步不同的任務,使用組是正確的。 – user3441734

1

@Rob對每個點都有非常好的細節建議。我注意到,當我用Rob的筆記調整Evan的代碼時,它似乎在Playground中起作用。我沒有在應用程序中測試過。注意如何在測試函數之外聲明組,以便稍後我們可以調用group.notify,我們可以在其中調用PlaygroundPage的finishExcution()。另外請注意,DispatchGroup的通知功能是在提交的任務對象完成後執行任何額外工作的好方法。在操場上,我們稱之爲通知:

import Foundation 
import PlaygroundSupport 
PlaygroundPage.current.needsIndefiniteExecution = true 

let queue = DispatchQueue.global(qos: .utility) 
let group = DispatchGroup() 

func test() { 
     queue.async(group: group) { 
     var a = [String]() 
     for i in 1...999 { 
      a.append(String(i)) 
      print("appending array a...") 
     } 
     print("a finished") 
     } 
     queue.async(group: group){ 
     var b = [String]() 
     for i in 1...999 { 
      b.append(String(i)) 
      print("appending array b...") 
     } 
     print("b finished") 
     } 

    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.01) { 
     print("doing clean up in timeout") 
    } 


} 

test() 
print("done") 

group.notify(queue: DispatchQueue.global()) { 
    print("work completed") 
    PlaygroundPage.current.finishExecution() 
} 
+0

此代碼沒有考慮到所請求的功能,這是等待所有作業完成,但不超過最大超時 – user3441734

0

只是爲了比較不同的方法試試這個在您的遊樂場

import Foundation 

func test(timeout: Double) { 
    let queue = DispatchQueue(label: "test", attributes: .concurrent) 
    let group = DispatchGroup() 
    var stop = false 
    let delay = timeout 

    queue.async(group: group) { 
     var str = [String]() 
     var i = 0 
     while i < 1000 && !stop{ 
      str.append(String(i)) 
      i += 1 
     } 

     print(1, "did", i, "iterations") 
    } 
    queue.async(group: group) { 
     var str = [String]() 
     var i = 0 
     while i < 2000 && !stop{ 
      str.append(String(i)) 
      i += 1 
     } 

     print(2, "did", i, "iterations") 
    } 
    queue.async(group: group) { 
     var str = [String]() 
     var i = 0 
     while i < 100 && !stop{ 
      str.append(String(i)) 
      i += 1 
     } 

     print(3, "did", i, "iterations") 
    } 
    queue.async(group: group) { 
     var str = [String]() 
     var i = 0 
     while i < 200 && !stop{ 
      str.append(String(i)) 
      i += 1 
     } 

     print(4, "did", i, "iterations") 
    } 
    group.wait(wallTimeout: .now() + delay) 
    stop = true 
    queue.sync(flags: .barrier) {} // to be sure there are no more jobs in my queue 
} 

var start = Date() 
test(timeout: 25.0) 
print("test done in", Date().timeIntervalSince(start), "from max 25.0 seconds") 
print() 
start = Date() 
test(timeout: 5.0) 
print("test done in", Date().timeIntervalSince(start), "from max 5.0 seconds") 

它打印(在我的環境)

3 did 100 iterations 
4 did 200 iterations 
1 did 1000 iterations 
2 did 2000 iterations 
test done in 17.7016019821167 from max 25.0 seconds 

3 did 100 iterations 
4 did 200 iterations 
2 did 697 iterations 
1 did 716 iterations 
test done in 5.00799399614334 from max 5.0 seconds