2016-10-11 50 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