簡體   English   中英

`sleep(_:)` vs `asyncAfter(deadline:execute:)`

[英]`sleep(_:)` vs `asyncAfter(deadline:execute:)`

我有一個值類型,需要相對較長的計算才能生成它(> 1s)。 我將此值類型包裝在一個枚舉中,該枚舉表示它是當前正在計算還是可用:

enum Calculatable<T> {
    case calculated(T), calculating
}

問題是這個值是持久的。 這意味着在長時間運行計算之后,當我獲得更新的值時,我會嘗試先將其持久化,然后再更改對業務邏輯可見的屬性。 如果持久化成功,則更新屬性,如果失敗,我想等待,然后再次嘗試持久化; 有個問題——如果計算的值依賴的任何其他值發生了變化,我們丟棄之前計算的值——停止嘗試持久化它——並向隊列提交一個新的Calculation操作——重新開始。 我最初的嘗試是這樣的:

/// Wraps a value that takes a long time to be calculated.
public class CalculatedValueWrapper<T> {

    /// The last submitted `Calculation` to the queue.
    private weak var latestCalculation: Optional<Operation>

    /// The long running calculation that produces the up to date, wrapped value.
    private let longCalculation: (_ cancelIf: () -> Bool) -> T

    /// Calculation queue.
    private let queue: OperationQueue

    /// The calculated value.
    private var wrappedValue: Calculatable<T>

    /// Update the wrapped value.
    public func update() {

        // Don't set if already being calculated.
        if case .calculated(_)=self.wrappedValue { self.wrappedValue = .calculating }

        // Initiate new calculation.
        self.latestCalculation?.cancel()
        self.latestCalculation={
            let calc: Calculation = .init(self, self.longCalculation)
            self.queue.addOperation(calc)
            return calc
        }()
    }
}

/// Executes a long running calculation and persists the result once complete.
class Calculation<T>: Operation {

    /// The calculation.
    private let calculation: (_ cancelIf: () -> Bool) -> T

    /// The owner of the wrapped value we're calculating.
    private weak var owner: Optional<CalculatedValueWrapper<T>>
    
    func main() {
        let result=self.calculation(cancelIf: { [unowned self] in self.isCancelled })

        let persist: () -> Bool={

            // In case the persistence fails.
            var didPersist=false

            // Persist the result on the main thread.
            DispatchQueue.main.sync { [unowned self] in

                // The owner may have been deallocated between the time this dispatch item was submitted and the time it began executing.
                guard let owner=self.owner else { self.cancel(); return }

                // May have been cancelled.
                guard !self.isCancelled else { return }

                // Attempt to persist the calculated result.
                if let _=try? owner.persist(result)  { 
                    didPersist=true 
                }
            }

            // Done.
            return didPersist
        }

        // Persist the new result. If it fails, and we're not cancelled, keep trying until it succeeds.
        while !self.isCancelled && !self.persist() {
            usleep(500_000)
        }
    }
}

我會堅持這一點,但經過進一步的研究,我注意到一種普遍的觀點,即在DispatchQueue items and Operations中休眠線程是不好的做法。 DispatchQueue為例,似乎被認為更好的替代方法是使用asyncAfter(deadline:execute:)啟動另一個DispatchQueue項。

在我的情況下,該解決方案似乎更復雜,因為能夠取消封裝所有需要完成的所有操作的單個操作使得取消變得容易。 我可以保存對最后執行的Calculation操作的引用,如果在主線程上提交另一個正在執行的操作,我取消舊的,添加新的,這樣我就知道舊的值不會被持久化,因為它是在執行取消后在主線程上完成的; 並且不得取消Calculation以使其保持其結果。

在這種情況下, DispatchQueuesOperationQueues是否會使替代解決方案更易於實施? 還是睡在這里完全沒問題?

做一個 RetryPersist NSOperation。 在將其添加到隊列之前,將其 isReady 設置為 false。 讓它執行 dispatchAfter 弱捕獲自身以將其 isReady 設置為 true。 現在您可以取消隊列中的任何 RetryPersist 操作,如果它們未准備好、未取消的計數不為零,您知道有一個等待 go 等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM