【问题标题】:Behavior for receive(on:) for DispatchQueue.mainDispatchQueue.main 的 receive(on:) 行为
【发布时间】:2023-10-04 10:07:01
【问题描述】:

从一个类中给出下面的代码:

cancellable = input
    .receive(on: scheduler)
    .map { ... }
    .sink(receiveValue: { value in
        self.state = value
    })

其中input 是 PassthroughSubject。

现在,当scheduler 是主队列或将从主线程调用 RunLoop.main AND 输入时,receive(on: scheduler) 是否会以编程方式优化对主队列的显式分派?

所以,基本上是这样的:

 if Thread.isMainThread { 
    /* execute closure */
 } else { 
    /* dispatch closure async to main */
 }

receive(on:) 的文档给出了一个模糊的提示,它可能会执行一些优化:

“首选接收(on:options :)而不是显式使用调度队列”

pub.sink {
    DispatchQueue.main.async {
        // Do something.
    }
}

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    不,receive(on:) 不会优化调度。这样做可能会导致僵局。示例:

    let l = Lock()
    
    let cancellable = input
        .receive(on: scheduler)
        .map { ... }
        .sink(receiveValue: { value in
            l.lock()
            self.state = value
            l.unlock()
        })
    
    l.lock()
    input.send(1)
    l.unlock()
    

    如果调度被消除,这个例子将尝试锁定已经锁定的锁l,然后挂起(如果它可以检测到死锁,则崩溃)。

    【讨论】:

    • 啊,我明白了——尽管我知道使用锁的示例仅用于说明。
    【解决方案2】:

    查看RunLoopDispatchQueue 的源代码,看起来它们对Scheduler 协议的一致性没有这样的优化。

    但公平地说,可能会有较低级别的优化在起作用。

    【讨论】:

    • 关于RunLoop,我不认为,它可以用任意线程实现,只能用“众所周知的”主线程。 DispatchQueue 没有返回当前队列的机制(调度前提条件除外)。所以,我认为,如果有任何代码进行了优化,它必须在receive(on:) 或Combine Scheduler 中。 ;)
    最近更新 更多