首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么在延迟调用时,循环迭代有时会被打乱?[代码和输出]

为什么在延迟调用时,循环迭代有时会被打乱?[代码和输出]
EN

Stack Overflow用户
提问于 2016-12-17 14:24:57
回答 2查看 73关注 0票数 0

我正在使用swift 3,来制作延迟事件,这是代码

代码语言:javascript
复制
public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void)
{
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel
{
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue
    {
        switch self
        {
            case .main:                 return DispatchQueue.main
            case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
            case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
            case .utility:              return DispatchQueue.global(qos: .utility)
            case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}



override func viewDidAppear(_ animated: Bool)
{

}

override func viewDidLoad()
{
    super.viewDidLoad()

    for i in 0..<20
    {
        delay(bySeconds: 1.0+Double(i)*0.5, dispatchLevel: .background)
        {
            print("__ \(i)")
            // delayed code that will run on background thread
        }
    }

}

实际输出,请注意模式在10之后的变化

代码语言:javascript
复制
__ 0
__ 1
__ 2
__ 3
__ 4
__ 5
__ 6
__ 7
__ 8
__ 9
__ 10
__ 12
__ 11
__ 14
__ 13
__ 16
__ 15
__ 17
__ 18
__ 19

预期输出

代码语言:javascript
复制
__ 0
__ 1
__ 2
__ 3
__ 4
__ 5
__ 6
__ 7
__ 8
__ 9
__ 10
__ 11
__ 12
__ 13
__ 14
__ 15
__ 16
__ 17
__ 18
__ 19

延时延时有问题吗?

EN

回答 2

Stack Overflow用户

发布于 2016-12-17 16:29:56

关键的响应是您正在使用异步函数和并发队列(后台)。

代码语言:javascript
复制
dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)

上面的代码将立即返回,它只是将任务设置为在未来的时间内执行,因此您的delay函数也将立即返回。因此,这不会阻止循环转到next (与sycn函数一致)。

另一方面,后台队列是一个concurrent队列,这意味着任务不会以添加到队列的相同顺序完成执行,这就解释了你得到的结果。

相反,如果您使用main-queue,因为它是一个串行队列,那么会有一个保证,它会按照添加的顺序逐个执行和完成。但是您将阻止应用程序的UI/响应性

票数 1
EN

Stack Overflow用户

发布于 2016-12-19 09:09:04

您假设当任务首先在并发队列上被调度时,它将首先被执行。并发队列将其任务分布在多个线程上,即使它们按顺序添加到每个线程,每个任务都会有一个随机延迟,这可能会导致它们乱序执行。

为了说明这一点,让我们将您的代码剥离到最低限度,并测量任务的调度时间和实际执行时间:

代码语言:javascript
复制
let queue = DispatchQueue.global(qos: .userInteractive)

for i in 0..<20 {
    let scheduledTime = DispatchTime.now() + Double(i) * 0.5
    queue.asyncAfter(deadline: scheduledTime) {
        let threadID = pthread_mach_thread_np(pthread_self()) // The thread that the task is executed on
        let executionTime = DispatchTime.now()
        let delay = executionTime.uptimeNanoseconds - scheduledTime.uptimeNanoseconds

        print(i, scheduledTime.uptimeNanoseconds, executionTime.uptimeNanoseconds, delay, threadID, separator: "\t")
    }
}

// Wait for all the tasks to complete. This is not how you should wait
// but this is just sample code to illustrate a point.
sleep(15)

结果,以纳秒为单位(添加了一些格式):

代码语言:javascript
复制
i     scheduledTime          executionTime          delay          threadID
0     142,803,882,452,582    142,803,883,273,138    820,556        3331
1     142,804,383,177,169    142,804,478,766,410    95,589,241     3331
2     142,804,883,221,388    142,804,958,658,498    75,437,110     3331
3     142,805,383,223,641    142,805,428,926,049    45,702,408     3331
4     142,805,883,224,792    142,806,066,279,866    183,055,074    3331
5     142,806,383,225,771    142,806,614,277,038    231,051,267    3331
6     142,806,883,229,494    142,807,145,347,839    262,118,345    3331
7     142,807,383,230,527    142,807,696,729,955    313,499,428    3331
8     142,807,883,231,420    142,808,249,459,465    366,228,045    3331
9     142,808,383,232,293    142,808,779,492,453    396,260,160    3331
10    142,808,883,233,183    142,809,374,609,495    491,376,312    3331
12    142,809,883,237,042    142,809,918,923,562    35,686,520     4355
11    142,809,383,234,072    142,809,918,923,592    535,689,520    3331
13    142,810,383,238,029    142,811,014,010,484    630,772,455    3331
14    142,810,883,238,910    142,811,014,040,582    130,801,672    4355
15    142,811,383,239,808    142,812,119,998,576    736,758,768    4355
16    142,811,883,240,686    142,812,120,019,559    236,778,873    3331
18    142,812,883,242,410    142,813,228,621,306    345,378,896    4355
17    142,812,383,241,550    142,813,228,646,734    845,405,184    3331
19    142,813,383,245,491    142,814,307,199,255    923,953,764    3331

这20个任务分布在2个线程(3331和4355)上。在每个线程中,任务是按顺序执行的,但是这些线程可能会被发送到不同的CPU核心,从而导致无序执行。每个任务也会随机延迟高达900ms。这就是使用队列的权衡:您无法控制延迟,因为谁知道这些全局队列上还运行着什么。这里有3个选项:

  • 如果计时非常关键,并且您希望在尽可能少的延迟的情况下执行任务,请创建并管理您自己的线程。
  • 通过引入延迟来使用串行而不是并发的依赖于queue.
  • Scheduling的任务,这通常不是一个好主意。查看NSOperationQueue,它允许您指定任务之间的依赖关系。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41195959

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档