濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > ruby中并發(fā)并行與全局鎖詳解

ruby中并發(fā)并行與全局鎖詳解

熱門標(biāo)簽:電話機(jī)器人電話卡封號(hào)怎么辦 浦東上海400開頭的電話申請(qǐng) 邯鄲外呼調(diào)研線路 武漢呼叫中心外呼系統(tǒng)線路商 樂(lè)昌電話機(jī)器人 真人語(yǔ)音電銷機(jī)器人系統(tǒng) 北京語(yǔ)音電銷機(jī)器人價(jià)格 開封百應(yīng)電銷機(jī)器人聯(lián)系方式 買了外呼系統(tǒng)不想用了怎么辦

前言

本文主要給大家介紹了關(guān)于ruby并發(fā)并行和全局鎖的相關(guān)內(nèi)容,分享出來(lái)供大家參考學(xué)習(xí),下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。

并發(fā)和并行

在開發(fā)時(shí),我們經(jīng)常會(huì)接觸到兩個(gè)概念: 并發(fā)和并行,幾乎所有談到并發(fā)和并行的文章都會(huì)提到一點(diǎn): 并發(fā)并不等于并行.那么如何理解這句話呢?

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

將這個(gè)例子擴(kuò)展到我們的web開發(fā)中, 就可以這樣理解:

  • 并發(fā):服務(wù)器同時(shí)收到了兩個(gè)客戶端發(fā)起的請(qǐng)求.
  • 順序執(zhí)行:服務(wù)器只有一個(gè)進(jìn)程(線程)處理請(qǐng)求,完成了第一個(gè)請(qǐng)求才能完成第二個(gè)請(qǐng)求,所以第二個(gè)請(qǐng)求就需要等待.
  • 并行執(zhí)行:服務(wù)器有兩個(gè)進(jìn)程(線程)處理請(qǐng)求,兩個(gè)請(qǐng)求都能得到響應(yīng),而不存在先后的問(wèn)題.

根據(jù)上述所描述的例子,我們?cè)?ruby 中怎么去模擬出這樣的一個(gè)并發(fā)行為呢? 看下面這一段代碼:

1、順序執(zhí)行:

模擬只有一個(gè)線程時(shí)的操作.

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)

上述代碼很簡(jiǎn)單,用 sleep 模擬耗時(shí)的操作.順序執(zhí)行時(shí)候的消耗時(shí)間.

2、并行執(zhí)行

模擬多線程時(shí)的操作

# 接上述代碼
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)

我們發(fā)現(xiàn)多線程下耗時(shí)和f1的耗時(shí)相近,這與我們預(yù)期的一樣,采用多線程可以實(shí)現(xiàn)并行.

Ruby 的多線程能夠應(yīng)付 IO Block,當(dāng)某個(gè)線程處于 IO Block 狀態(tài)時(shí),其它的線程還可以繼續(xù)執(zhí)行,從而使整體處理時(shí)間大幅縮短.

Ruby 中的線程

上述的代碼示例中使用了 ruby 中 Thread 的線程類, Ruby可以很容易地寫Thread類的多線程程序.Ruby線程是一個(gè)輕量級(jí)的和有效的方式,以實(shí)現(xiàn)在你的代碼的并行.

接下來(lái)來(lái)描述一段并發(fā)時(shí)的情景

 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 "現(xiàn)在需要等3秒才可以看到我:#{Time.now - time}"
 end
 test
 ## 不用等3秒就可以看到我:8.6e-05
 ## 現(xiàn)在需要等3秒才可以看到我:3.003699

Thread的創(chuàng)建是非阻塞的,所以文字立即就可以輸出.這樣就模擬了一個(gè)并發(fā)的行為.每個(gè)線程sleep 3 秒,在阻塞的情況下,多線程可以實(shí)現(xiàn)并行.

那么這個(gè)時(shí)候我們是不是就完成了并行的能力呢?

很遺憾,我上述的描述中只是提到了我們?cè)诜亲枞那闆r下可以模擬了并行.讓我們?cè)倏匆幌聞e的例子:

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)

從這里可以看出,即便我們將同一個(gè)任務(wù)分成了4個(gè)線程并行,但是時(shí)間并沒(méi)有減少,這是為什么呢?

因?yàn)橛腥宙i(GIL)的存在!?。?/p>

全局鎖

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

即便我們希望使用多線程來(lái)實(shí)現(xiàn)代碼的并行, 由于這個(gè)全局鎖的存在, 每次只有一個(gè)線程能夠執(zhí)行代碼,至于哪個(gè)線程能夠執(zhí)行, 這個(gè)取決于底層操作系統(tǒng)的實(shí)現(xiàn)。

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

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

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

但是我們之前sleep的時(shí)候, 明明實(shí)現(xiàn)了并行?。?/p>

這個(gè)就是Ruby設(shè)計(jì)高級(jí)的地方——所有的阻塞操作是可以并行的,包括讀寫文件,網(wǎng)絡(luò)請(qǐng)求在內(nèi)的操作都是可以并行的.

require 'benchmark'
require 'net/http'

# 模擬網(wǎng)絡(luò)請(qǐng)求
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)

在網(wǎng)絡(luò)請(qǐng)求時(shí)程序發(fā)生了阻塞,而這些阻塞在Ruby的運(yùn)行下是可以并行的,所以在耗時(shí)上大大縮短了.

GIL 的思考

那么,既然有了這個(gè)GIL鎖的存在,是否意味著我們的代碼就是線程安全了呢?

很遺憾不是的,GIL 在ruby 執(zhí)行中會(huì)某一些工作點(diǎn)時(shí)切換到另一個(gè)工作線程去,如果共享了一些類變量時(shí)就有可能踩坑.

那么, GIL 在 ruby代碼的執(zhí)行中什么時(shí)候會(huì)切換到另外一個(gè)線程去工作呢?

有幾個(gè)明確的工作點(diǎn):

  • 方法的調(diào)用和方法的返回, 在這兩個(gè)地方都會(huì)檢查一下當(dāng)前線程的gil的鎖是否超時(shí),是否要調(diào)度到另外線程去工作
  • 所有io相關(guān)的操作, 也會(huì)釋放gil的鎖讓其它線程來(lái)工作
  • 在c擴(kuò)展的代碼中手動(dòng)釋放gil的鎖
  • 還有一個(gè)比較難理解, 就是ruby stack 進(jìn)入 c stack的時(shí)候也會(huì)觸發(fā)gil的檢測(cè)

一個(gè)例子

@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 ,即每個(gè)線程時(shí)都能保留好當(dāng)前的 @c 的值.沒(méi)有線程簡(jiǎn)的調(diào)度.

如果在上述代碼線程中加入 可能會(huì)觸發(fā)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]]

這個(gè)就會(huì)觸發(fā)GIL的lock, 數(shù)據(jù)異常了.

小結(jié)

Web 應(yīng)用大多是 IO 密集型的,利用 Ruby 多進(jìn)程+多線程模型將能大幅提升系統(tǒng)吞吐量.其原因在于:當(dāng)Ruby 某個(gè)線程處于 IO Block 狀態(tài)時(shí),其它的線程還可以繼續(xù)執(zhí)行,從而降低 IO Block 對(duì)整體的影響.但由于存在 Ruby GIL (Global Interpreter Lock),MRI Ruby 并不能真正利用多線程進(jìn)行并行計(jì)算.

PS. 據(jù)說(shuō) JRuby 去除了GIL,是真正意義的多線程,既能應(yīng)付 IO Block,也能充分利用多核 CPU 加快整體運(yùn)算速度,有計(jì)劃了解一些.

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

標(biāo)簽:六安 松原 淄博 鄂州 石嘴山 宜春 河北 自貢

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《ruby中并發(fā)并行與全局鎖詳解》,本文關(guān)鍵詞  ruby,中,并發(fā),并行,與,全局,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《ruby中并發(fā)并行與全局鎖詳解》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于ruby中并發(fā)并行與全局鎖詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    潢川县| 永年县| 仁布县| 拉萨市| 东辽县| 景宁| 丹凤县| 永靖县| 东丽区| 石狮市| 嵊州市| 卓资县| 兴山县| 新安县| 石台县| 永丰县| 尚志市| 青海省| 喜德县| 舟山市| 荥阳市| 保康县| 庆阳市| 鄂温| 达拉特旗| 启东市| 岳阳县| 咸宁市| 广丰县| 德兴市| 当雄县| 临桂县| 安平县| 柳江县| 乡宁县| 新闻| 成武县| 大城县| 虎林市| 靖江市| 鹿邑县|