防止同时执行任务

Prevent from executing tasks in same time

我需要有关如何在我的应用程序中处理 oauth2 令牌刷新的建议。

我正在使用 GRPC 向我的 API 发出 http 请求,我使用 oauth2 令牌(它在 1 小时后过期)连接。

在启动每个请求之前,我检查令牌:

似乎一切正常,但在某些情况下,我的客户 "lose" 令牌。

问题是对于 2 个请求 A 和 B 在完全相同的时间 发起 ,如果令牌已过时,它们都会刷新它。 我的服务器会生成一个newTokenA,return它,生成newTokenB(去掉newTokenA),return它。 如果响应 newTokenA 在 newTokenB 之后到达客户端 ,则客户端令牌令牌将不是好的令牌。

我使用了Semaphore来确保同时调用refreshToken。

但是当我的信号量正在等待时,我没有收到来自服务器的任何响应。

let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)

func authenticate(completion: (GRPCProtoCall) -> (Void)) -> GRPCProtoCall {

    // Wait here if authenticate already called
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

     // If token up to date
    if isOAuth2TokenValid() {
        dispatch_semaphore_signal(semaphore) // Release semaphore
        completion(self.withAuthorization())
        return self
    }

    // Refresh the outdated token
    APIClient.shared.oAuth2AccessTokenRefresh { (response) -> (Void) in
        dispatch_semaphore_signal(semaphore)  // Release semaphore
        completion(self.withAuthorization())
    }

    return self
}

首先,我建议为 1 资源创建信号量,而不是为 0(用于阅读目的)创建信号量:

let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0)

其次,我认为问题是你先释放信号量,然后调用完成块:做这样的事情:

// Refresh the outdated token
APIClient.shared.oAuth2AccessTokenRefresh { (response) -> (Void) in
    completion(self.withAuthorization())
    dispatch_semaphore_signal(semaphore)  // Release semaphore at very end
}

我认为你的 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 持有你的线程,你可以尝试超时以防最后一个请求没有响应,并将其放在 return self

之前
while semaphore.wait(timeout: DispatchTime.now() + Double(5000000000) / Double(NSEC_PER_SEC)) == DispatchTimeoutResult.success {//time out set to 5 seconds
    print("wait")
}

为什么不使该方法完全异步? (self 反正好像知道)

func authenticate(completion: (GRPCProtoCall) -> ()) {

    // If token up to date
    if isOAuth2TokenValid() {
        completion(self.withAuthorization())
    } else {
    // Refresh the outdated token
        APIClient.shared.oAuth2AccessTokenRefresh { response in
           completion(self.withAuthorization())
        }
    }
}