如何创建队列并手动启动它

How to create queue and start it manually

在我的应用程序中,我必须实现刷新令牌逻辑。我希望在刷新令牌过程中,所有发送的请求都保存在一个队列中,一旦我的过程完成,我就启动队列

例如,我想要这样的东西:

let queue = DispatchQueue(label: "myQueue", attributes: .concurrent)  

queue.async {
    // request One
}

queue.async {
    // request Two
}

当刷新令牌过程完成时:

queue.send()

你可以像这样构建一个class

class ConcurrentQueue {

    typealias Task = () -> ()
    private var tasks: [Task] = []
    private let serialQueue = DispatchQueue(label: "Serial queue")

    func enqueueTask(_ task: @escaping Task) {
        serialQueue.sync {
            tasks.append(task)
        }
    }

    func runAndRemoveAllTasks() {
        serialQueue.sync {
            tasks.forEach { task in
                task()
            }
            tasks.removeAll()
        }
    }
}

这 class 允许您入队多个 Task(s)

Every Task is a closure like this () -> Void.

当您将一个 Task 加入队列时,它不会被执行,它只是附加到内部数组。

let concurrentQueue = ConcurrentQueue()

concurrentQueue.enqueueTask {
    print("1")
}

concurrentQueue.enqueueTask {
    print("2")
}

concurrentQueue.enqueueTask {
    print("3")
}

这段代码的结果只是保存了3个任务,它们并没有被执行。

然后当你打电话时

concurrentQueue.runAndRemoveAllTasks()

所有任务都已执行并在控制台中得到

1
2
3

线程安全

方法 enqueueTask(_)runAndRemoveAllTasks() 是线程安全的。

事实上,它们仅在

内与内部 tasks 数组(不是隐式线程安全的)交互
serialQueue.sync {
   ...
}

这保证了对 tasks 数组的一致访问。

解决方案是使用 BlockOperation:

let pendingRequests = BlockOperation()

pendingRequests.addExecutionBlock {
    //Adding first request
}

pendingRequests.addExecutionBlock {
    //Adding second request
}

刷新令牌完成时:

pendingRequests.start()

In my application I have to implement a refresh token logic. I would like that during the refresh token process all requests sent be kept in a queue and as soon as my process is finished I start the queue

如果你想创建一个队列并延迟其任务的开始,只需暂停它,例如:

let queue = DispatchQueue(label: "myQueue", attributes: .concurrent)  
queue.suspend()

queue.async {
    // request One
}

queue.async {
    // request Two
}

fetchToken { result in
    switch result {
    case .success(let token):
        // do something with token
        print(token)
        queue.resume()

    case .failure(let error):
        // handle the error
        print(error)
    }
}

这就是您 suspend and resume 调度队列的方式。请注意,suspend 仅阻止项目在队列中启动,但对已经 运行 的任务没有影响。这就是为什么我在向其发送项目之前暂停队列。

但是以上内容引出了您在 failure 场景中想要做什么的问题。你只有一个队列,里面有一堆计划好的任务。从理论上讲,您可以保留对那些分派块的引用(通过使用 DispatchWorkItem 模式而不是简单的闭包,并且您可以 cancel 这些项目),但我可能会到达一个操作队列,例如

let queue = OperationQueue()
queue.isSuspended = true

queue.addOperation {
    // request One
}

queue.addOperation {
    // request Two
}

fetchToken { result in
    switch result {
    case .success(let token):
        // do something with token
        print(token)
        queue.isSuspended = false

    case .failure(let error):
        // handle the error
        print(error)
        queue.cancelAllOperations()
    }
}

这和上面的一样,但是我们可以用cancelAllOperations取消所有排队的操作。


顺便说一下,您可以创建自定义 Operation 子类来处理本身是异步的任务。我假设您的“请求一”和“请求二”是异步网络请求。请参阅 looking for a specific example where Operation is preferred over GCD or vice-versa 以讨论何时可能更喜欢 OperationQueue 而不是 DispatchQueue