如何知道ruby中哪些不是线程安全的?

如何知道ruby中哪些不是线程安全的?,ruby,multithreading,concurrency,thread-safety,ruby-on-rails-4,Ruby,Multithreading,Concurrency,Thread Safety,Ruby On Rails 4,,默认情况下,所有内容都必须在线程化环境中运行。这意味着我们编写的所有代码和所有我们使用的gem都必须是线程安全的 因此,我有几个问题: ruby/rails中什么是线程不安全的Vsruby/rails中的线程安全是什么 是否有已知的线程安全的宝石列表或反之亦然 是否有非线程安全的常见代码模式列表示例@result | |=some_method ruby lang core中的数据结构(如Hashetc)是线程安全的吗 在MRI上,有一个表示一次只能运行一个ruby线程(除了IO),线程安全更

,默认情况下,所有内容都必须在线程化环境中运行。这意味着我们编写的所有代码所有我们使用的gem都必须是
线程安全的

因此,我有几个问题:

  • ruby/rails中什么是线程不安全的Vsruby/rails中的线程安全是什么
  • 是否有已知的线程安全的宝石列表或反之亦然
  • 是否有非线程安全的常见代码模式列表示例
    @result | |=some_method
  • ruby lang core中的数据结构(如
    Hash
    etc)是线程安全的吗
  • 在MRI上,有一个表示一次只能运行一个ruby线程(除了
    IO
    ),线程安全更改是否会影响我们
    没有一个核心数据结构是线程安全的。据我所知,Ruby附带的唯一一个是标准库中的队列实现(
    require'thread';q=queue.new

    MRI的GIL不能使我们免于线程安全问题。它只确保两个线程不能同时运行Ruby代码,即在两个不同的CPU上同时运行。线程仍然可以在代码中的任意点暂停和恢复。如果你写的代码像
    @n=0;3.times{Thread.start{100.times{@n+=1}}}
    。GIL或多或少是对单核系统的模拟,它不会改变编写正确并发程序的基本问题

    即使MRI像Node.js一样是单线程的,您仍然需要考虑并发性。使用递增变量的示例可以很好地工作,但是您仍然可以获得竞争条件,其中事件以不确定的顺序发生,并且一个回调会破坏另一个回调的结果。单线程异步系统更容易推理,但它们并非没有并发问题。试想一个有多个用户的应用程序:如果两个用户或多或少同时点击堆栈溢出帖子上的edit,请花一些时间编辑该帖子,然后点击save,第三个用户稍后在阅读同一帖子时会看到其更改

    在Ruby中,与大多数其他并发运行时一样,任何多于一个操作的操作都不是线程安全的<代码>@n+=1
  • 不是线程安全的,因为它是多个操作
    @n=1
    是线程安全的,因为它是一个操作(这是许多隐藏的操作,如果我试图详细描述为什么它是“线程安全的”,我可能会遇到麻烦,但最终你不会从分配中得到不一致的结果)<代码>@n | |=1
    ,不是,也没有其他速记操作+赋值。我犯过很多次的一个错误是编写
    return,除非@start@started=true
    ,这根本不是线程安全的

    我不知道Ruby的线程安全和非线程安全语句的权威列表,但有一个简单的经验法则:如果一个表达式只执行一个(无副作用)操作,那么它可能是线程安全的。例如:
    a+b
    是可以的,
    a=b
    也是可以的,
    a.foo(b)
    是可以的,如果方法
    foo
    没有副作用(因为Ruby中几乎任何东西都是方法调用,在许多情况下甚至是赋值,其他例子也是如此)。在这种情况下,副作用意味着改变状态的事情<代码>def foo(x)@x=x;结束并非没有副作用

    在Ruby中编写线程安全代码最困难的一点是,所有核心数据结构,包括数组、哈希和字符串,都是可变的。很容易不小心泄露你状态的一部分,当那部分是可变的时,事情会变得非常糟糕。考虑下面的代码:

    class Thing
      attr_reader :stuff
    
      def initialize(initial_stuff)
        @stuff = initial_stuff
        @state_lock = Mutex.new
      end
    
      def add(item)
        @state_lock.synchronize do
          @stuff << item
        end
      end
    end
    
    find_stuff
    在第一次使用时可以正常工作,但在第二次使用时会返回其他内容。为什么?
    load\u things
    方法碰巧认为它拥有传递给它的选项哈希,并执行
    color=options.delete(:color)
    。现在,
    标准选项
    常量不再具有相同的值。常量仅在它们引用的内容中是常量,它们不能保证它们引用的数据结构的恒定性。试想一下,如果这段代码同时运行会发生什么

    如果您避免共享可变状态(例如,多线程访问的对象中的实例变量、多线程访问的散列和数组等数据结构),那么线程安全就不那么难了。尽量减少并发访问的应用程序部分,并集中精力。IIRC,在Rails应用程序中,每个请求都会创建一个新的控制器对象,因此它只会被单个线程使用,从该控制器创建的任何模型对象也是如此。但是,Rails也鼓励使用全局变量(
    User.find(…)
    使用全局变量
    User
    ,您可能认为它只是一个类,它是一个类,但也是全局变量的命名空间),其中一些是安全的,因为它们是只读的,但有时您会将内容保存在这些全局变量中,因为这样做很方便。当你使用任何全球可访问的东西时,要非常小心

    在线程环境中运行Rails已经有一段时间了,所以如果不是Rails专家,我仍然可以说,在Rails本身方面,您不必担心线程安全。通过做我上面提到的一些事情,您仍然可以创建线程不安全的Rails应用程序。当它出现时,其他gem会假设它们不是线程安全的,除非它们说它们是,如果它们说它们是线程安全的,那么就假设它们不是,并检查它们的代码(但仅仅因为您看到它们是线程安全的)
    STANDARD_OPTIONS = {:color => 'red', :count => 10}
    
    def find_stuff
      @some_service.load_things('stuff', STANDARD_OPTIONS)
    end