【问题标题】:Swift semaphore behaviorSwift 信号量行为
【发布时间】:2021-02-16 07:53:33
【问题描述】:

我有问题。我需要一个可以使用的资源。我已经实现了这个机制:

func shouldRetry(request: Request, error: Error) -> Bool {
            let semaphore = DispatchSemaphore(value: 0)
            
            var shouldRetry: Bool = false
            self.interceptor?.retry(request.response, for: self.session!, dueTo: error, completion: { (retryResult) in
                switch retryResult {
                case .retry:
                    shouldRetry = true
                    semaphore.signal()
                case .doNotRetry:
                    shouldRetry = false
                    semaphore.signal()
                case .retryWithDelay(let delay):
                    shouldRetry = true
                    self.retryTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { (_) in
                        semaphore.signal()
                        self.retryTimer?.invalidate()
                        self.retryTimer = nil
                    })
                }
            })
            
            semaphore.wait()
            return shouldRetry
        
    }

问题是interceptor?.retry func use 是在信号量的同一个线程中调用的,这会阻塞进程。有什么建议吗?

更新:

我解决了我的问题。我继承了 URLProtocol 抽象类。这个类与 URLSession 一起使用,它可以暂停 HTTP 调用,制作异步代码并恢复

【问题讨论】:

  • 你想达到什么目的? Semaphore.wait 阻塞线程直到获得 semaphore.signal,这是预期的行为
  • shouldRetry 返回一个用于在条件下重试发布自身的组合扩展中的 Bool。在此之前,我调用了一个执行异步代码的重试方法,然后调用了一个带有枚举的闭包(retry,retryWithDelay,doNotRetry)。在返回布尔之前,我等待关闭(我正在尝试复制 Alamofire 拦截器)。一切正常,但是如果再次进行 HTTP 调用,重试方法使用相同的线程,并且所有管道都卡住了。
  • 您“只是”需要从同步设计切换到异步设计。不幸的是,您可能必须从头开始。所以你的“所有作品”的说法是相当乐观的;)

标签: swift concurrency grand-central-dispatch semaphore


【解决方案1】:

如果没有更多上下文,我认为没有答案。但是,如果你尝试实现一个“刷新令牌适配器/重试器”,策略如下:

您需要一个专门的适配器(结构或类),它使用访问令牌设置授权标头。访问令牌必须是线程安全的,甚至获取令牌也可能失败——例如,因为没有令牌。如果发生错误,适配器会尝试不获取新的访问令牌,这将在重试器中完成。适配器可能只是没有设置访问令牌。请求将失败,将在重试器中处理。

适配器调度是专用队列上的函数,我们称之为“process_queue”。完成设置标头后,它会调用完成处理程序。

您还需要一个专门的 Retrier(结构或类)。它访问保存令牌请求结果的共享状态。例如,这可以是 Swift.Result。

此重试器的函数在专用调度队列上执行。

专门的 Retrier 确定响应状态,如果状态码是 401(未授权)并且如果 Authorization 标头是不记名令牌,则需要发出刷新令牌请求。

在启动此刷新令牌任务时,它会暂停 process_queue,以便不再使用过期的访问令牌进行请求适配。

现在,令牌请求完成并表示成功。重试器存储访问令牌(到密钥链中),用新的访问令牌更新原始请求,然后恢复 process_queue,然后调用其完成处理程序。

正确的错误处理使这稍微复杂一些。问题是当刷新令牌请求失败时如何处理挂起的请求。我使用了一种方法,它让所有排队的未决请求失败,直到刷新令牌请求完成。它们只是因错误 401 而失败。然后开始一个新的循环,从尝试获取新的刷新令牌开始。

这种方法可以防止多个失败的请求同时调用令牌端点。当刷新令牌也过期时,您甚至可以扩展该方法。在这种情况下,您需要登录用户。所有这些都可能发生在重试器中。它仅在登录完成、获取新访问令牌完成或发生错误时完成。

【讨论】:

  • Al 机制是明确的!但我不明白如何使用 combine
  • 你的函数shouldRetry(request:error:) 应该返回一个Publisher——或者可能是一个Future。
猜你喜欢
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 2011-07-29
  • 1970-01-01
相关资源
最近更新 更多