0

0

详解ruby中并发并行与全局锁代码分享

巴扎黑

巴扎黑

发布时间:2017-09-13 09:44:48

|

1722人浏览过

|

来源于php中文网

原创

最近在学习ruby,想着将自己学习的内容总结一下分享出来,下面这篇文章主要给大家介绍了关于ruby中并发并行与全局锁的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。

前言

本文主要给大家介绍了关于ruby并发并行和全局锁的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

并发和并行

在开发时,我们经常会接触到两个概念: 并发和并行,几乎所有谈到并发和并行的文章都会提到一点: 并发并不等于并行.那么如何理解这句话呢?

  • 并发: 厨师同时接收到了2个客人点了的菜单需要处理.

  • 顺序执行: 如果只有一个厨师,那么他只能一个菜单接着一个菜单的去完成.

  • 并行执行: 如果有两个厨师,那么就可以并行,两个人一起做菜.

将这个例子扩展到我们的web开发中, 就可以这样理解:

  • 并发:服务器同时收到了两个客户端发起的请求.

  • 顺序执行:服务器只有一个进程(线程)处理请求,完成了第一个请求才能完成第二个请求,所以第二个请求就需要等待.

  • 并行执行:服务器有两个进程(线程)处理请求,两个请求都能得到响应,而不存在先后的问题.

根据上述所描述的例子,我们在 ruby 中怎么去模拟出这样的一个并发行为呢? 看下面这一段代码:

1、顺序执行:

模拟只有一个线程时的操作.


require 'benchmark'

def f1
 puts "sleep 3 seconds in f1\n"
 sleep 3
end

def f2
 puts "sleep 2 seconds in f2\n"
 sleep 2 
end

Benchmark.bm do |b|
 b.report do
 f1
 f2
 end 
end
## 
## user  system  total  real
## sleep 3 seconds in f1
## sleep 2 seconds in f2
## 0.000000 0.000000 0.000000 ( 5.009620)

上述代码很简单,用 sleep 模拟耗时的操作.顺序执行时候的消耗时间.

2、并行执行

模拟多线程时的操作


# 接上述代码
Benchmark.bm do |b|
 b.report do
 threads = []
 threads << Thread.new { f1 }
 threads << Thread.new { f2 }
 threads.each(&:join)
 end 
end
##
## user  system  total  real
## sleep 3 seconds in f1
## sleep 2 seconds in f2
## 0.000000 0.000000 0.000000 ( 3.005115)

我们发现多线程下耗时和f1的耗时相近,这与我们预期的一样,采用多线程可以实现并行.

Ruby 的多线程能够应付 IO Block,当某个线程处于 IO Block 状态时,其它的线程还可以继续执行,从而使整体处理时间大幅缩短.

Ruby 中的线程

上述的代码示例中使用了 ruby 中 Thread 的线程类, Ruby可以很容易地写Thread类的多线程程序.Ruby线程是一个轻量级的和有效的方式,以实现在你的代码的并行.

接下来来描述一段并发时的情景


 def thread_test
 time = Time.now
 threads = 3.times.map do 
  Thread.new do
  sleep 3 
  end
 end
 puts "不用等3秒就可以看到我:#{Time.now - time}"
 threads.map(&:join)
 puts "现在需要等3秒才可以看到我:#{Time.now - time}"
 end
 test
 ## 不用等3秒就可以看到我:8.6e-05
 ## 现在需要等3秒才可以看到我:3.003699

Thread的创建是非阻塞的,所以文字立即就可以输出.这样就模拟了一个并发的行为.每个线程sleep 3 秒,在阻塞的情况下,多线程可以实现并行.

那么这个时候我们是不是就完成了并行的能力呢?

很遗憾,我上述的描述中只是提到了我们在非阻塞的情况下可以模拟了并行.让我们再看一下别的例子:


require 'benchmark'
def multiple_threads
 count = 0
 threads = 4.times.map do 
 Thread.new do
  2500000.times { count += 1}
 end
 end
 threads.map(&:join)
end

def single_threads
 time = Time.now
 count = 0
 Thread.new do
 10000000.times { count += 1}
 end.join
end

Benchmark.bm do |b|
 b.report { multiple_threads }
 b.report { single_threads }
end
##  user  system  total  real
## 0.600000 0.010000 0.610000 ( 0.607230)
## 0.610000 0.000000 0.610000 ( 0.623237)

从这里可以看出,即便我们将同一个任务分成了4个线程并行,但是时间并没有减少,这是为什么呢?

因为有全局锁(GIL)的存在!!!

PHP 网络编程技术与实例(曹衍龙)
PHP 网络编程技术与实例(曹衍龙)

PHP网络编程技术详解由浅入深,全面、系统地介绍了PHP开发技术,并提供了大量实例,供读者实战演练。另外,笔者专门为本书录制了相应的配套教学视频,以帮助读者更好地学习本书内容。这些视频和书中的实例源代码一起收录于配书光盘中。本书共分4篇。第1篇是PHP准备篇,介绍了PHP的优势、开发环境及安装;第2篇是PHP基础篇,介绍了PHP中的常量与变量、运算符与表达式、流程控制以及函数;第3篇是进阶篇,介绍

下载

全局锁

我们通常使用的ruby采用了一种称之为GIL的机制.

即便我们希望使用多线程来实现代码的并行, 由于这个全局锁的存在, 每次只有一个线程能够执行代码,至于哪个线程能够执行, 这个取决于底层操作系统的实现。

即便我们拥有多个CPU, 也只是为每个线程的执行多提供了几个选择而已。

我们上面代码中每次只有一个线程可以执行 count += 1 .

Ruby 多线程并不能重复利用多核 CPU,使用多线程后整体所花时间并不缩短,反而由于线程切换的影响,所花时间可能还略有增加。

但是我们之前sleep的时候, 明明实现了并行啊!

这个就是Ruby设计高级的地方——所有的阻塞操作是可以并行的,包括读写文件,网络请求在内的操作都是可以并行的.


require 'benchmark'
require 'net/http'

# 模拟网络请求
def multiple_threads
 uri = URI("http://www.baidu.com")
 threads = 4.times.map do 
 Thread.new do
  25.times { Net::HTTP.get(uri) }
 end
 end
 threads.map(&:join)
end

def single_threads
 uri = URI("http://www.baidu.com")
 Thread.new do
 100.times { Net::HTTP.get(uri) }
 end.join
end

Benchmark.bm do |b|
 b.report { multiple_threads }
 b.report { single_threads }
end

 user  system  total  real
0.240000 0.110000 0.350000 ( 3.659640)
0.270000 0.120000 0.390000 ( 14.167703)

在网络请求时程序发生了阻塞,而这些阻塞在Ruby的运行下是可以并行的,所以在耗时上大大缩短了.

GIL 的思考

那么,既然有了这个GIL锁的存在,是否意味着我们的代码就是线程安全了呢?

很遗憾不是的,GIL 在ruby 执行中会某一些工作点时切换到另一个工作线程去,如果共享了一些类变量时就有可能踩坑.

那么, GIL 在 ruby代码的执行中什么时候会切换到另外一个线程去工作呢?

有几个明确的工作点:

  • 方法的调用和方法的返回, 在这两个地方都会检查一下当前线程的gil的锁是否超时,是否要调度到另外线程去工作

  • 所有io相关的操作, 也会释放gil的锁让其它线程来工作

  • 在c扩展的代码中手动释放gil的锁

  • 还有一个比较难理解, 就是ruby stack 进入 c stack的时候也会触发gil的检测

一个例子


@a = 1
r = []
10.times do |e|

Thread.new {
 @c = 1
 @c += @a
 r << [e, @c]
}
end
r
## [[3, 2], [1, 2], [2, 2], [0, 2], [5, 2], [6, 2], [7, 2], [8, 2], [9, 2], [4, 2]]

上述中r 里 虽然e的前后顺序不一样, 但是@c的值始终保持为 2 ,即每个线程时都能保留好当前的 @c 的值.没有线程简的调度.

如果在上述代码线程中加入 可能会触发GIL的操作 例如 puts 打印到屏幕:


@a = 1
r = []
10.times do |e|

Thread.new {
 @c = 1
 puts @c
 @c += @a
 r << [e, @c]
}
end
r
## [[2, 2], [0, 2], [4, 3], [5, 4], [7, 5], [9, 6], [1, 7], [3, 8], [6, 9], [8, 10]]

这个就会触发GIL的lock, 数据异常了.

小结

Web 应用大多是 IO 密集型的,利用 Ruby 多进程+多线程模型将能大幅提升系统吞吐量.其原因在于:当Ruby 某个线程处于 IO Block 状态时,其它的线程还可以继续执行,从而降低 IO Block 对整体的影响.但由于存在 Ruby GIL (Global Interpreter Lock),MRI Ruby 并不能真正利用多线程进行并行计算.

PS. 据说 JRuby 去除了GIL,是真正意义的多线程,既能应付 IO Block,也能充分利用多核 CPU 加快整体运算速度,有计划了解一些.

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

32

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

23

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

16

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

5

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

6

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

268

2026.01.31

高干文在线阅读网站大全
高干文在线阅读网站大全

汇集热门1v1高干文免费阅读资源,涵盖都市言情、京味大院、军旅高干等经典题材,情节紧凑、人物鲜明。阅读专题下面的文章了解更多详细内容。

195

2026.01.31

无需付费的漫画app大全
无需付费的漫画app大全

想找真正免费又无套路的漫画App?本合集精选多款永久免费、资源丰富、无广告干扰的优质漫画应用,涵盖国漫、日漫、韩漫及经典老番,满足各类阅读需求。阅读专题下面的文章了解更多详细内容。

170

2026.01.31

漫画免费在线观看地址大全
漫画免费在线观看地址大全

想找免费又资源丰富的漫画网站?本合集精选2025-2026年热门平台,涵盖国漫、日漫、韩漫等多类型作品,支持高清流畅阅读与离线缓存。阅读专题下面的文章了解更多详细内容。

85

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Ruby中文文档
Ruby中文文档

共0课时 | 0人学习

极客学院Ruby基础视频教程
极客学院Ruby基础视频教程

共22课时 | 4.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号