简体   繁体   中英

Swift: Multithreading with Completion Handler

I have a function that accesses a critical variable and needs mutually exclusive access to it, but returns via completion handler.

This is what I am currently using to attempt this:

static func getAccessTokenValue(completionHandlerResult : @escaping (_ accesstoken:String) -> ()) {
    tokenQ.sync {
        let expire_in_str =  UserDefaults.standard.value(forKey: "expires_in") as! String
        print(expire_in_str)
        let accessToken_time = UserDefaults.standard.value(forKey: "access_token_time") as! Date

        let todayDate = Date()
        let seconds = (Calendar.current as NSCalendar).components(.second, from: accessToken_time, to: todayDate, options: []).second
        if  seconds! < Int(5) {
            print("success")
            completionHandlerResult( Constants.Access_Token)
        }
        else {
            refreshLock.wait()
            self.renewAccessToken(completionHandler: { accesstokenValue in
                tokenQ.sync {
                    completionHandlerResult(accesstokenValue)
                }
            })
            refreshLock.signal()
        }
    }
}

The goal is for only one thread to be able to use self.renewAccessToken at a time. Using DispatchQueue.sync alone was not accomplishing this so I moved to semaphores but the above code results in a deadlock as it appears that the refreshLock.signal() has no effect.

self.renewAccessToken uses alamofire and has another completion handler within it, which is adding to the threading problems.

The main issue I am running into is multiple threads are trying to renew the same token at the same time and once one thread renews the token the other threads keep the old token and their renewal requests are denied because a token can only be renewed once.

What would be the best way to handle this scenario?

Try this:

static func getAccessTokenValue(completionHandlerResult : @escaping (_ accesstoken:String) -> ()) {
    tokenQ.sync {
        let expire_in_str =  UserDefaults.standard.value(forKey: "expires_in") as! String
        let accessToken_time = UserDefaults.standard.value(forKey: "access_token_time") as! Date

        let todayDate = Date()
        let seconds = (Calendar.current as NSCalendar).components(.second, from: accessToken_time, to: todayDate, options: []).second
        if  seconds! < Int(5) {
            completionHandlerResult( Constants.Access_Token)
        }
        else {
            refreshLock.wait()
            self.renewAccessToken(completionHandler: { accesstokenValue in
                    completionHandlerResult(accesstokenValue)
            })
            refreshLock.signal()
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM