一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Ruby - ruby中并發并行與全局鎖詳解

ruby中并發并行與全局鎖詳解

2020-05-13 10:27雙槍老漢 Ruby

最近在學習ruby,想著將自己學習的內容總結一下分享出來,下面這篇文章主要給大家介紹了關于ruby中并發并行與全局鎖的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。

前言

本文主要給大家介紹了關于ruby并發并行和全局鎖的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

并發和并行

在開發時,我們經常會接觸到兩個概念: 并發和并行,幾乎所有談到并發和并行的文章都會提到一點: 并發并不等于并行.那么如何理解這句話呢?

  • 并發: 廚師同時接收到了2個客人點了的菜單需要處理.
  • 順序執行: 如果只有一個廚師,那么他只能一個菜單接著一個菜單的去完成.
  • 并行執行: 如果有兩個廚師,那么就可以并行,兩個人一起做菜.

將這個例子擴展到我們的web開發中, 就可以這樣理解:

  • 并發:服務器同時收到了兩個客戶端發起的請求.
  • 順序執行:服務器只有一個進程(線程)處理請求,完成了第一個請求才能完成第二個請求,所以第二個請求就需要等待.
  • 并行執行:服務器有兩個進程(線程)處理請求,兩個請求都能得到響應,而不存在先后的問題.

根據上述所描述的例子,我們在 ruby 中怎么去模擬出這樣的一個并發行為呢? 看下面這一段代碼:

1、順序執行:

模擬只有一個線程時的操作.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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、并行執行

模擬多線程時的操作

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 接上述代碼
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線程是一個輕量級的和有效的方式,以實現在你的代碼的并行.

接下來來描述一段并發時的情景

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 秒,在阻塞的情況下,多線程可以實現并行.

那么這個時候我們是不是就完成了并行的能力呢?

很遺憾,我上述的描述中只是提到了我們在非阻塞的情況下可以模擬了并行.讓我們再看一下別的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)的存在!!!

全局鎖

我們通常使用的ruby采用了一種稱之為GIL的機制.

即便我們希望使用多線程來實現代碼的并行, 由于這個全局鎖的存在, 每次只有一個線程能夠執行代碼,至于哪個線程能夠執行, 這個取決于底層操作系統的實現。

即便我們擁有多個CPU, 也只是為每個線程的執行多提供了幾個選擇而已。

我們上面代碼中每次只有一個線程可以執行 count += 1 .

Ruby 多線程并不能重復利用多核 CPU,使用多線程后整體所花時間并不縮短,反而由于線程切換的影響,所花時間可能還略有增加。

但是我們之前sleep的時候, 明明實現了并行啊!

這個就是Ruby設計高級的地方——所有的阻塞操作是可以并行的,包括讀寫文件,網絡請求在內的操作都是可以并行的.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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的檢測

一個例子

?
1
2
3
4
5
6
7
8
9
10
11
12
@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 打印到屏幕:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@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 加快整體運算速度,有計劃了解一些.

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://segmentfault.com/a/1190000010211352

延伸 · 閱讀

精彩推薦
  • Ruby剖析 Ruby 訪問控制

    剖析 Ruby 訪問控制

    前面,我們說 Ruby 沒有函數,只有方法.而且實際上有不止一種方法.這一節我們介紹 訪問控制 (accesscontrols). 想想當我們在最高層而不是在一個類的定義里定義...

    ruby教程網3572020-04-08
  • RubyRuby進行文件信息輸出實例代碼

    Ruby進行文件信息輸出實例代碼

    Ruby進行文件信息輸出實例代碼,數據是隨機的,所以每次的記錄都會不同。 ...

    ruby教程網2962020-04-10
  • RubyRuby設計模式編程中使用Builder建造者模式的實例

    Ruby設計模式編程中使用Builder建造者模式的實例

    這篇文章主要介紹了Ruby設計模式編程中使用Builder建造者模式的實例,建造者模式將一個復雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表...

    范孝鵬2192020-05-07
  • RubyRuby簡潔學習筆記(一):字符串、數字、類和對象

    Ruby簡潔學習筆記(一):字符串、數字、類和對象

    這篇文章主要介紹了Ruby簡潔學習筆記(一):字符串、數字、類和對象,本文是學習筆記第一篇,需要的朋友可以參考下 ...

    腳本之家2472020-04-20
  • RubyRuby迭代器的7種技巧分享

    Ruby迭代器的7種技巧分享

    這篇文章主要介紹了Ruby迭代器的7種技巧分享,Ruby中的迭代器非常人性化,本文既是講解了7個技巧也是講解了7種迭代器,需要的朋友可以參考下 ...

    腳本之家4782020-04-20
  • RubyRuby環境下安裝使用bundler來管理多版本的gem

    Ruby環境下安裝使用bundler來管理多版本的gem

    這篇文章主要介紹了Ruby環境下安裝使用bundler來管理多版本的gem的方法,舉了Ruby On Rails中的應用實例來進行演示,需要的朋友可以參考下 ...

    日拱一卒4332020-05-10
  • Ruby簡要說明Ruby中的迭代器

    簡要說明Ruby中的迭代器

    這篇文章主要介紹了Ruby中的迭代器,迭代器的概念在動態語言的編程中十分重要,文章中介紹了Ruby中的each迭代器和collect迭代器,需要的朋友可以參考下 ...

    goldensun2772020-04-25
  • RubyCentOS中配置Ruby on Rails環境

    CentOS中配置Ruby on Rails環境

    經過一個上午的折騰,終于把ROR環境在CentOS中搞定,繞了很多彎路,把文章寫下來總結一下 ...

    可樂加糖4762020-04-12
主站蜘蛛池模板: 亚洲haose在线观看 | 四虎com| 91在线亚洲精品一区 | 亚洲欧美成人综合在线 | 性欧美videosex18嫩 | 美女禁区视频免费观看精选 | 亚洲精品国产综合久久一线 | 国产91在线免费 | 小早川怜子在线播放精品 | 护士xxxx| 亚欧有色在线观看免费版高清 | 77成人影视| 黄色cc| 亚洲国产福利精品一区二区 | 福利片成人午夜在线 | 麻豆网站在线免费观看 | 国产午夜视频在线观看网站 | 四虎4hu永久免费国产精品 | 精品久久久久亚洲 | yellow视频免费观看播放 | 成人永久免费 | 成人免费观看一区二区 | www.青青草原 | 95视频在线观看在线分类h片 | 美女扒开两腿露出尿口的视频 | 高h细节肉爽文办公室 | 成人资源在线观看 | chinese野外gay军人 | 精品欧美小视频在线观看 | 性关系视频免费网站在线观看 | 91无套极品外围在线播放 | 男女做污事| 好姑娘完整版在线观看中文 | a在线观看欧美在线观看 | 99久久er这里只有精品17 | 四川女人偷人真实视频 | 亚洲国产精品久久精品成人网站 | 亚洲网站在线播放 | 青青草原国产一区二区 | 奇米影视一区 | 国产高清视频免费最新在线 |