并发队列问题 - iOS/Swift

3

在我的程序中,我需要同时在后台运行两个任务。为了实现这一点,我使用了以下并发队列:

let concurrentQueue = DispatchQueue(label: "concurrentQueue", qos: .utility, attributes: .concurrent)

concurrentQueue.async {
    for i in 0 ..< 10{
        print(i)
    }
}

concurrentQueue.async {
    for i in (0 ..< 10).reversed(){
        print(i)
    }
}

我需要的输出结果如下:

0
9
1
8
2
7
3
6
4
5
5
4
6
3
7
2
8
1
9
0

但我得到的是这样的:

enter image description here

我参考了以下教程,以便了解Swift 3中的并发队列:
https://www.appcoda.com/grand-central-dispatch/

有人能告诉我我的代码哪里出了问题吗?还是这个结果就是我应该得到的结果?是否有其他方法可以完成我的任务?任何帮助都将不胜感激。


如果我猜测,每个异步调用都在等待工作完成。你的代码和示例之间的主要区别在于,代码仍在异步执行,而循环在主线程中执行。 - Sam Marion
他们还在两个独立的队列上执行此操作,而您只在一个队列上执行。创建对同一对象的引用,并尝试在currentQueue1上异步执行。 - Sam Marion
2
这可能只是一个时间问题 - 每个操作执行多长时间在另一个操作开始之前没有保证。为了测试这一点,在循环中放置一个随机的睡眠调用,以查看输出如何更改。类似于 usleep( arc4random() % 100000 ) - EricS
@SamMarion 这怎么可能?“for”循环是在主线程中运行的吗?我不明白。而且我已经按照教程做了。 - Hanushka Suren
1
@GMHSJ for循环在执行时可以相互中断,但这并不意味着它们一定会相互中断。如果循环只需要几毫秒的时间,完全有可能在另一个循环开始之前就完成了。更有趣的是,你可以连续运行代码1000次得到一个结果,然后在第1001次运行时得到不同的结果。这使得调试变得具有挑战性。 - EricS
显示剩余4条评论
2个回答

4

您的代码示例没有问题。这是将两个任务提交到并发队列的正确语法。

问题在于您可能不一定会看到它们同时运行。有两个问题可能会影响到这一点:

  1. The first dispatched task can run so quickly that it just happens to finish before the second task gets going. If you slow them down a bit, you'll see your concurrent behavior:

    let concurrentQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".concurrentQueue", qos: .utility, attributes: .concurrent)
    
    concurrentQueue.async {
        for i in 0 ..< 10 {
            print("forward: ", i)
            Thread.sleep(forTimeInterval: 0.1)
        }
    }
    
    concurrentQueue.async {
        for i in (0 ..< 10).reversed() {
            print("reversed:", i)
            Thread.sleep(forTimeInterval: 0.1)
        }
    }
    

    You'd never sleep in production code, but for pedagogical purposes, it can better illustrate the issue.

    You can also omit the sleep calls, and just increase the numbers dramatically (e.g. 1_000 or 10_000), and you might start to see concurrent processing taking place.

  2. Your device could be resource constrained, preventing it from running the code concurrently. Devices have a limited number of CPU cores to run concurrent tasks. Just because you submitted the tasks to concurrent queue, it doesn't mean the device is capable of running the two tasks at the same time. It depends upon the hardware and what else is running on that device.

    By the way, note that you might see different behavior on the simulator (which is using your Mac's CPU, which could be running many other tasks) than on a device. You might want to make sure to test this behavior on an actual device, if you're not already.

请注意,你提到你“需要”输出在两个队列之间交替使用print语句。虽然你的教程中的屏幕截图表明这应该是可以预期的,但你绝对没有保证它会发生。
如果你真的需要它们来回交替,你必须添加一些机制来协调它们。你可以使用信号量(我不愿意建议这样做,因为它们是新开发人员特别容易出问题的常见问题来源)或具有依赖关系的操作队列。

我会尝试你的建议并进行检查。我理解你的意思,但我认为这不是因为我使用的设备而发生的事情,在我的情况下。我的意思是,为了得到这个输出,我不需要并发队列,只需使用一个全局队列,在其中这两个for循环可以进行。顺便说一下,感谢你的帮助,朋友。 - Hanushka Suren
在我将东西放入for循环中后,它按照我预期的方式工作了。是的,这是一个时间问题。谢谢@Rob。 - Hanushka Suren

1
也许你可以尝试使用信号量。
    let semaphore1 = DispatchSemaphore(value: 1)
    let semaphore2 = DispatchSemaphore(value: 0)
    concurrentQueue.async {
    for i in 0 ..< 10{
    semaphore1.wait()
    print(i)
    semaphore2.signal()
    }
    }

    concurrentQueue.async {

    for i in (0 ..< 10).reversed(){
    semaphore2.wait()
    print(i)
    semaphore1.signal()
    }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接