如果一个线程等待阻塞I/O,Ruby互斥体允许两个线程并发执行吗?
这就是我对GIL在核磁共振成像中的作用的理解。我很好奇互斥和吉尔之间是否有什么区别?
发布于 2020-02-25 23:35:21
默认实现(MRI)上的全局解释器锁定(Global解释器Lock )阻止任何两个Ruby线程并发运行。Mutex阻止特定线程并发运行,其他线程可以任意运行。
注意,在没有GIL的实现中,规则是不同的,比如JRuby,线程可以独立和并发地运行。
实际上,Global解释器锁本身就是一个互斥锁,但它是默认使用的,而不是在代码中显式地使用,比如synchronize。
如果一个线程在等待互斥对象时被阻塞,那么其他线程继续正常运行,它们能够独立地完成他们需要的任何事情。
全局解释器锁也不同,因为您的Ruby线程将被周期性地中断,以便其他线程能够运行。这是为了防止一个线程垄断锁。
发布于 2020-02-25 22:50:20
是的,这个很管用。正因为如此,在MRI中使用线程实际上对许多工作负载是有用的,即使只有一个线程可以同时执行“代码”。
一个常见的例子是web应用程序,比如Rails应用程序。在这里,您可以在一个进程中运行多个线程,例如Puma,每个线程处理一个请求。因为您经常在这里等待数据库,所以也可以执行不同的线程。这是因为数据库适配器(例如mysql2或pg)通过调用数据库释放GIL,并在应答到达并传递给调用方时重新获取它。
但是,使用Mutex,您可以确保某个代码块一次只由一个线程执行。一个常见的例子是加法器:
class Adder
attr_reader :number
def initialize
@number = 0
end
def add(number)
new_number = @number
new_number = new_number + number
@number = new_number
# The above code is extremely verbose to show what's happening here.
# It is equivalent to
# @number += number
end
end在这里,Adder#add方法不是线程保存。如果多个线程试图同时添加数字,一些更新将丢失,因为该操作不是原子操作(而是由读、操作和写组成)。使用add周围的Mutex,您可以确保操作在一个线程中完成,并且共享数据结构得到一致更新。
作为一般建议,如果您正在读取或更新跨线程边界共享的任何数据,则应该始终使用Mutex。为了确保正确性,您还应该严格控制哪些数据结构跨越线程边界传递,并在可能的情况下加以避免。
如果您仍然需要这样做,并发红宝石 gem提供了一些线程安全的数据结构,可以帮助跨线程边界共享数据。
https://stackoverflow.com/questions/60403209
复制相似问题