Swift 相当于 Ruby 的 Concurrent::Event?

Posted

技术标签:

【中文标题】Swift 相当于 Ruby 的 Concurrent::Event?【英文标题】:Swift equivalent of Ruby’s Concurrent::Event? 【发布时间】:2021-11-11 16:18:10 【问题描述】:

流行的 Concurrent-Ruby 库有一个我觉得很棒的 Concurrent::Event class。它非常巧妙地封装了“一些线程需要等待另一个线程完成某事才能继续执行”的思想。

只需三行代码即可使用:

一个创建对象 拨打.wait开始等待,然后 事情准备就绪后,可以致电.set

您需要使用其他并发原语创建它的所有锁和布尔值都已为您处理好了。

引用一些文档以及示例用法:

老派内核风格的事件让人想起 C++ 中的 Win32 编程。

创建Event 时,它处于unset 状态。线程可以选择 #wait 事件,阻塞直到被另一个线程释放。当一个 线程想要提醒它调用#set 方法的所有阻塞线程 然后将唤醒所有听众。一旦设置了Event,它就会保持设置状态。 调用#wait 的新线程将立即返回。

require 'concurrent-ruby'

event = Concurrent::Event.new

t1 = Thread.new do
  puts "t1 is waiting"
  event.wait
  puts "event ocurred"
end

t2 = Thread.new do
  puts "t2 calling set"
  event.set
end

[t1, t2].each(&:join)

打印输出如下

t1 is waiting
t2 calling set
event occurred

(由于它是多线程的,因此可能有几种不同的顺序,但“t2调用集”总是在“事件发生”之前出现。)


ios 上的 Swift 中有类似的东西吗?

【问题讨论】:

这类似于在并行调度队列上使用调度工作,由 disoatch 组协调。但这并不是一个非常有趣的模式,它非常低级且容易出错。你最好使用 RxSwift/Combine 中的 observable/publishers 【参考方案1】:

您可以使用 Grand Central Dispatch DispatchSemaphore 在您的示例中实现结果 - 这是一个传统的计数信号量。每次调用signal 都会增加信号量。每次调用wait 都会减少信号量,如果结果小于零,它会阻塞并等待信号量为 0

let semaphore = DispatchSemaphore(value: 0)

let q1 = DispatchQueue(label:"q1", target: .global(qos: .utility))

let q2 = DispatchQueue(label:"q2", target: .global(qos: .utility))

q1.async 
   print("q1 is waiting")
   semaphore.wait()
   print("event occurred")


q2.async 
   print("q2 calling signal")
   semaphore.signal()

输出:

q1 正在等待

q2 调用信号

事件发生

但是如果你有多个线程要等待,这个对象将不起作用。由于对wait 的每次调用都会减少信号量,因此其他任务将保持阻塞状态。

为此,您可以使用DispatchGroup。在组中开始任务之前调用enter,完成后调用leave。您可以使用wait 阻塞直到组为空,并且像您的Ruby 对象一样,如果组已经为空并且多个线程可以wait 在同一个组上,wait 将不会阻塞。

let group = DispatchGroup()

let q1 = DispatchQueue(label:"q1", target: .global(qos: .utility))

let q2 = DispatchQueue(label:"q2", target: .global(qos: .utility))

q1.async 
    print("q1 is waiting")
    group.wait()
        print("event occurred")


group.enter()
q2.async 
    print("q2 calling leave")
    group.leave()

输出:

q1 正在等待

q2 打电话请假

事件发生

如果可能,您通常希望避免在 iOS 上阻塞线程,因为存在死锁的风险,并且如果您阻塞主线程,您的整个应用程序将变得无响应。更常见的是使用notify 来安排代码在组变为空时执行。

我了解您的代码只是一个人为的示例,但根据您实际想要做什么以及您支持的最低 iOS 要求,可能会有更好的选择。

当使用notify 而不是wait 完成多个异步任务时,DispatchGroup 执行代码 在管道中组合处理异步事件 (iOS 13+) 异步/等待 (iOS 15+)

【讨论】:

【参考方案2】:

我认为最接近这一点的是 Swift 5.5 中的新 async/await 语法。没有 event.set 的等价物,但 await 等待异步完成。并发的一个特别好的表达是async let,它同时进行,然后让您暂停以收集async let 调用的所有结果:

async let result1 = // do something asynchronous
async let result2 = // do something else asynchronous at the same time
// ... and keep going...
// now let's gather up the results
return await (result1, result2)

【讨论】:

以上是关于Swift 相当于 Ruby 的 Concurrent::Event?的主要内容,如果未能解决你的问题,请参考以下文章

Swift中类似C++和ruby中的final机制

如何让 JSON 中的日期格式在 ruby 与 Swift 间保持一致

如何让 JSON 中的日期格式在 ruby 与 Swift 间保持一致

Swift中实现ruby中字符串乘法倍增的功能

相当于 Ruby 中的“继续”

Ruby 相当于 JavaScript 的 encodeURIComponent 产生相同的输出? [复制]