当前位置: 首页 > 面试题库 >

试图理解异步操作子类

夏侯兴怀
2023-03-14
问题内容

我正在尝试Operation在辅助项目中使用s,而不是在整个网络代码中乱扔基于闭包的回调,以帮助消除嵌套调用。因此,我在阅读有关该主题的文章时,遇到了此实现:

open class AsynchronousOperation: Operation {

    // MARK: - Properties

    private let stateQueue = DispatchQueue(label: "asynchronous.operation.state", attributes: .concurrent)

    private var rawState = OperationState.ready

    private dynamic var state: OperationState {
        get {
            return stateQueue.sync(execute: {
                rawState
            })
        }
        set {
            willChangeValue(forKey: "state")
            stateQueue.sync(flags: .barrier, execute: {
                rawState = newValue
            })
            didChangeValue(forKey: "state")
        }
    }

    public final override var isReady: Bool {
        return state == .ready && super.isReady
    }

    public final override var isExecuting: Bool {
        return state == .executing
    }

    public final override var isFinished: Bool {
        return state == .finished
    }

    public final override var isAsynchronous: Bool {
        return true
    }


    // MARK: - NSObject

    private dynamic class func keyPathsForValuesAffectingIsReady() -> Set<String> {
        return ["state"]
    }

    private dynamic class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
        return ["state"]
    }

    private dynamic class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
        return ["state"]
    }


    // MARK: - Foundation.Operation

    public final override func start() {
        super.start()

        if isCancelled {
            finish()
            return
        }

        state = .executing
        execute()
    }


    // MARK: - Public

    /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.
    open func execute() {
        fatalError("Subclasses must implement `execute`.")
    }

    /// Call this function after any work is done or after a call to `cancel()` to move the operation into a completed state.
    public final func finish() {
        state = .finished
    }
}

@objc private enum OperationState: Int {

    case ready

    case executing

    case finished
}

Operation我希望对该子类有一些实现细节,以帮助他们理解。

  1. stateQueue物业的目的是什么?我看到它是由计算属性使用getsetstate,但是我找不到任何说明它们使用的sync:flags:executesync:execute方法的文档。

  2. NSObject返回部分中的三个类方法的目的是什么["state"]?我看不到它们在任何地方都被使用过。我在中找到了NSObjectclass func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>但似乎并不能帮助我理解为什么要声明这些方法。


问题答案:

你说:

1.
stateQueue物业的目的是什么?我看到state计算属性的get和set正在使用它,但是找不到任何解释它们使用的sync:flags:executesync:execute方法的文档。


此代码“同步”对属性的访问以使其线程安全。至于为什么你需要做的是,看到的Operation文件,其中建议:

多核注意事项

…当您继承子类时NSOperation,必须确保从多个线程中调用的所有重写方法都可以安全使用。如果在子类中实现自定义方法(例如自定义数据访问器),则还必须确保这些方法是线程安全的。因此,必须同步访问操作中的任何数据变量,以防止潜在的数据损坏。有关同步的更多信息,请参见《线程编程指南》。

关于此并发队列用于同步的确切使用,这称为“读取器-写入器”模式。读取器-
写入器模式的基本概念是,读取可以相对于彼此并发发生(因此sync,没有障碍),但是绝对不能相对于该属性的任何其他访问同时进行写入(因此async有障碍)。

例如,您可以像这样在数组上实现用于线程安全的读写器:

class ThreadSafeArray<T> {
    private var values: [T]
    private let queue = DispatchQueue(label: "...", attributes: .concurrent)

    init(_ values: [T]) {
        self.values = values
    }

    func reader<U>(block: () throws -> U) rethrows -> U {
        return try queue.sync {
            try block()
        }
    }

    func writer(block: @escaping (inout [T]) -> Void) {
        queue.async(flags: .barrier) {
            block(&self.values)
        }
    }

    // e.g. you might use `reader` and `writer` like the following:

    subscript(_ index: Int) -> T {
        get { reader { values[index] } }
        set { writer { $0[index] = newValue } }
    }

    func append(_ value: T) {
        writer { $0.append(value) }
    }

    func remove(at index: Int) {
        writer { $0.remove(at: index)}
    }
}

显然,在此Operation子类中使用reader-writer 更加简单,但是上面说明了这种模式。

您还问:

2.
NSObject返回部分中的三个类方法的目的是什么["state"]?我看不到它们在任何地方都被使用过。我在中找到了NSObjectclass func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>但似乎并不能帮助我理解为什么要声明这些方法。

这些是确保变更的只是方法state的属性的属性触发志愿通知isReadyisExecuting以及isFinished。这三个键的KVO通知对于异步操作的正确运行至关重要。无论如何,《键值观察编程指南:注册从属键》中概述了该语法。

keyPathsForValuesAffectingValue您找到的方法是相关的。您可以使用该方法注册依赖键,也可以使用原始代码段中所示的各个方法。

顺便说一句,这是AsynchronousOperation您提供的课程的修订版,即:

  1. 您不得致电super.start()。如start文档所述(强调):

如果要实现并发操作,则必须重写此方法并使用它来启动操作。 您的自定义实现不得super在任何时候调用。

  1. @objc在Swift 4中html" target="_blank">添加必需项。

  2. 重命名execute为use main,这是Operation子类的约定。

  3. 声明isReadyfinal属性是不合适的。任何子类都应有权进一步完善其isReady逻辑(尽管我们承认很少这样做)。

  4. 使用#keyPath,使代码更安全/健壮。

  5. 使用dynamic属性时,您无需手动进行KVO通知。在此示例中,不需要手动调用willChangeValuedidChangeValue

  6. 进行更改finish,使其仅在.finished尚未完成时才进入状态。

从而:

public class AsynchronousOperation: Operation {

    /// State for this operation.

    @objc private enum OperationState: Int {
        case ready
        case executing
        case finished
    }

    /// Concurrent queue for synchronizing access to `state`.

    private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent)

    /// Private backing stored property for `state`.

    private var _state: OperationState = .ready

    /// The state of the operation

    @objc private dynamic var state: OperationState {
        get { return stateQueue.sync { _state } }
        set { stateQueue.async(flags: .barrier) { self._state = newValue } }
    }

    // MARK: - Various `Operation` properties

    open         override var isReady:        Bool { return state == .ready && super.isReady }
    public final override var isExecuting:    Bool { return state == .executing }
    public final override var isFinished:     Bool { return state == .finished }
    public final override var isAsynchronous: Bool { return true }

    // KVN for dependent properties

    open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
        if ["isReady", "isFinished", "isExecuting"].contains(key) {
            return [#keyPath(state)]
        }

        return super.keyPathsForValuesAffectingValue(forKey: key)
    }

    // Start

    public final override func start() {
        if isCancelled {
            state = .finished
            return
        }

        state = .executing

        main()
    }

    /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.

    open override func main() {
        fatalError("Subclasses must implement `main`.")
    }

    /// Call this function to finish an operation that is currently executing

    public final func finish() {
        if !isFinished { state = .finished }
    }
}


 类似资料:
  • 概述 定时器 Promise 对象

  • 我在单元测试(用C#编写)中存根了必要的方法等,但问题是在断言测试之前异步操作没有完成。 我怎么才能绕过这个?我应该创建一个模拟TaskFactory或任何其他技巧来单元测试异步操作吗?

  • import { QuoteComponent } from './quote.component'; import { provide } from '@angular/core'; import { async, TestBed, fakeAsync, tick, } from '@angular/core/testing'; class MockQuoteService {

  • 下面的这些操作符属于单独的rxjava-async模块,它们用于将同步对象转换为Observable。 start( ) — 创建一个Observable,它发射一个函数的返回值 toAsync( ) or asyncAction( ) or asyncFunc( ) — 将一个函数或者Action转换为已Observable,它执行这个函数并发射函数的返回值 startFuture( ) — 将

  • 单线程模型 单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。 注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合。 JavaScript 之所以采

  • 如果这被认为是一个可接受的实践,我需要什么-如果有-错误处理?我的理解是,task.wait()将重新抛出异步操作抛出的任何异常,并且我没有提供任何取消异步操作的机制。仅仅调用task.wait()就足够了吗?