防止完成处理程序同步执行 [英] Preventing completion handler to be executed synchronously

查看:80
本文介绍了防止完成处理程序同步执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Swift中编写一些网络代码,以防止启动已经进行的下载.通过在(同步的)数组A中跟踪网络请求的身份以及关联的完成处理程序,可以做到这一点.当网络调用结束时,它将调用与该资源关联的完成处理程序,随后它将从数组A中删除这些处理程序.

I'm writing some networking code in Swift that prevents initiating a download that is already in progress. I do this by keeping tracking of the identity of the network request along with the associated completion handlers in an (synchronized) array A. When a network call finishes it calls the completion handlers that are associated with that resource and it subsequently removes those handlers from the array A.

在某些情况下,我想确保线程无法访问数组.例如,请考虑以下情形:

I want to make sure there is no way for threads to access the array in certain cases. For example, consider the following scenario:

  1. 开始下载资源X的请求.
  2. 验证是否已发出请求.
  3. 将完成处理程序添加到数组A.
  4. 如果尚未发出请求,请开始下载.
  1. A request to download resource X is started.
  2. Verify whether the request has already been made.
  3. Add the completion handler to the array A.
  4. If request has not been made, start the download.

如果资源X已经在下载,并且此下载的完成处理程序中断了步骤2和3之间的线程,该怎么办?已经验证了已经发出请求,因此下载将不会开始,但是新的完成处理程序将添加到数组A中,该数组将永远不会被调用.

What if resource X was already downloading, and the completion handler for this download interrupts the thread between steps 2 and 3? It has been verified that the request has been made so the download will not be started, but the new completion handler will be added to array A which will now never be called.

我将如何阻止这种情况的发生?在执行第2步和第3步时可以锁定数组以进行写入吗?

How would I block this from happening? Can I lock the array for writing while I do steps 2 and 3?

推荐答案

我正在假设您希望能够添加多个回调,这些回调将在最新请求完成时运行,无论是否已在其中-是否飞行.

I'm working on the assumption that you want to be able to add multiple callbacks that will all be run when the latest request completes, whether it was already in-flight or not.

这是解决方案的草图.基本要点是在触摸处理程序数组之前先进行锁定,无论是添加一个还是在请求完成后调用它们.您还必须与是否完全相同的锁 同步是否启动新请求的确定.

Here's a sketch of a solution. The basic point is to take a lock before touching the array(s) of handlers, whether to add one or to invoke them after the request has completed. You must also synchronize the determination of whether to start a new request, with the exact same lock.

如果在添加处理程序的公共方法中已经持有该锁,并且请求自己的完成运行,则后者必须等待前者,并且您将具有确定性的行为(将调用新的处理程序).

If the lock is already held in the public method where the handlers are added, and the request's own completion runs, then the latter must wait for the former, and you will have deterministic behavior (the new handler will be invoked).

class WhateverRequester
{
    typealias SuccessHandler = (Whatever) -> Void
    typealias FailureHandler = (Error) -> Void

    private var successHandlers: [SuccessHandler] = []
    private var failureHandlers: [FailureHandler] = []

    private let mutex = // Your favorite locking mechanism here.

    /** Flag indicating whether there's something in flight */
    private var isIdle: Bool = true

    func requestWhatever(succeed: @escaping SuccessHandler,
                         fail: @escaping FailureHandler)
    {
        self.mutex.lock()
        defer { self.mutex.unlock() }

        self.successHandlers.append(succeed)
        self.failureHandlers.append(fail)

        // Nothing to do, unlock and wait for request to finish
        guard self.isIdle else { return }

        self.isIdle = false
        self.enqueueRequest()
    }

    private func enqueueRequest()
    {
        // Make a request however you do, with callbacks to the methods below
    }

    private func requestDidSucceed(whatever: Whatever)
    {
        // Synchronize again before touching the list of handlers and the flag
        self.mutex.lock()
        defer { self.mutex.unlock() }

        for handler in self.successHandlers {
            handler(whatever)
        }

        self.successHandlers = []
        self.failureHandlers = []
        self.isIdle = true
    }

    private func requestDidFail(error: Error)
    {
        // As the "did succeed" method, but call failure handlers
        // Again, lock before touching the arrays and idle flag.
    }
} 

这是如此广泛的适用范围,以至于您实际上可以将回调存储,锁定和调用提取到其自己的通用组件中,请求者"类型可以创建,拥有和使用该通用组件.

This is so broadly applicable that you can actually extract the callback storage, locking, and invocation into its own generic component, which a "Requester" type can create, own, and use.

这篇关于防止完成处理程序同步执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆