【发布时间】:2019-09-17 03:05:38
【问题描述】:
使用 stabby lambda 或 proc 调用 ruby/rails CONSTANTS 是否线程安全?
我知道在 ruby 社区中不鼓励将常量定义为动态方法,因为常量定义本身不是线程安全的,并且可能像全局变量一样受到竞争条件的影响。
但是,如果您定义一个常量来接收一个 stabby lambda 或 proc,这会使代码线程安全吗?
例如这篇博文 on defining constants using procs/lambda's 建议定义为 lambda 和 procs 的常量可以被信任以每次都可靠地唯一调用:
class Foo
LAMBDA = lambda {Time.now}
PROC = proc {Time.now}
STABBYLAMBDA = ->{Time.now}
BEGINBLOCK = begin Time.now end
def Foo.the_time
Time.now
end
METHODBLOCK = Foo.the_time
end
Foo::LAMBDA[]
# => 2019-09-16 22:39:51 -0700
Foo::LAMBDA[]
# => 2019-09-16 22:39:53 -0700
Foo::PROC[]
# => 2019-09-16 22:39:55 -0700
Foo::PROC[]
# => 2019-09-16 22:39:57 -0700
Foo::STABBYLAMBDA[]
# => 2019-09-16 22:39:59 -0700
Foo::STABBYLAMBDA[]
# => 2019-09-16 22:40:01 -0700
Foo::BEGINBLOCK
# => 2019-09-16 22:39:51 -0700
Foo::BEGINBLOCK
# => 2019-09-16 22:39:51 -0700
Foo::METHODBLOCK
# => 2019-09-16 22:39:51 -0700
Foo::METHODBLOCK
# => 2019-09-16 22:39:51 -0700
但这是否也扩展到线程安全?
例如在这段代码中,我有一个 Rails 服务对象,它在多线程 Sidekiq 中运行,它提取用于上传 CSV 的数据:
class ExportReferralData < ServiceObject
EXPORT_COLUMNS = ["Referrer_phone_number", "Referrer_ID", "Referee_phone_number", "Referee_ID", "Date_Approved"].freeze
EXPORT_ROW = ->(referral){
[referral.customer.identifier, referral.customer.accountable.internal_identifier, referral.friend.identifier, referral.friend.accountable.internal_identifier, referral.approved_at.strftime("%Y%m%d%H%M")]
}
def initialize
end
def call
data = data_for_csv( Referral.all )
UploadFile.put(file_path) do |file|
create_csv(file, data)
end
end
private
def data_for_csv(active_record_query_result)
active_record_query_result.inject([]) do |result, item|
result << EXPORT_ROW.call(item)
end
end
def create_csv(file, export_data)
CSV(file, headers:true, force_quotes: true) do |csv|
csv << EXPORT_COLUMNS
export_data.each do |export_row|
csv << export_row
end
end
file.rewind
end
def file_path
"upload/APPROVED_#{Time.current.strftime("%Y%m%d%H%M")}.csv"
end
end
这个多线程代码将 EXPORT_ROW 定义为一个常量,它等于一个 stabby lambda(不是一个 proc),然后它采用它传递的任何 referral ActiveRecord 对象来动态构建每个 CSV 行。 lambda 中的这个局部变量然后调用 ActiveRecord 查询。
从风格上讲,我喜欢这段代码的外观,但我想确保我在这里使用 EXPORT_ROW.call(item) 实际上是线程安全的,尤其是在调用 ActiveRecord 查询等方法时。
非常感谢任何帮助!
【问题讨论】:
-
不知道,但你为什么不直接使用 Mutex 来确定呢?
-
在这里使用互斥锁会是什么样子?
-
“定义为 lambda(但不是 procs)的常量可以被信任每次都可靠地唯一调用”——我不知道这意味着什么,也无法在文章中找到任何提及。您使用 proc 得到两个相同结果的事实只是意味着您执行它们的速度太快了,所以没有时间……呃,时间……来改变。这篇文章也完全不是关于线程安全的。所有
Proc对象(无论是通过lambda、proc还是->获得)都是不可变的,因此它们对线程安全的影响为零(尽管它们的内容当然可以是线程安全的)。跨度> -
@Amadan 感谢您指出这一点,我再次在本地运行代码并更新了我的示例。我只确认
Foo::BEGINBLOCK和Foo::METHODBLOCK无法重新调用Time.now。 -
两者都不是
Proc;它们是执行您给它们的表达式的结果(在本例中为Date)。
标签: ruby-on-rails ruby multithreading csv constants