เธรดใน Ruby คืออะไร
เธรดทำให้โปรแกรม Ruby ของคุณทำหลายๆ อย่างพร้อมกันได้
สิ่งที่ชอบ :
- การอ่านหลายไฟล์
- การจัดการคำขอเว็บหลายรายการ
- สร้างการเชื่อมต่อ API หลายรายการ
จากการใช้เธรด คุณจะมีโปรแกรม Ruby แบบมัลติเธรด ซึ่งสามารถทำงานให้เสร็จเร็วขึ้น
แต่คำเตือน…
ใน MRI (Ruby Interpreter ของ Matz) วิธีเริ่มต้นในการเรียกใช้แอปพลิเคชัน Ruby คุณจะได้รับประโยชน์จากเธรดเมื่อเรียกใช้ แอปพลิเคชันที่ผูกกับ i/o .
ข้อจำกัดนี้มีอยู่เนื่องจาก GIL (Global Interpreter Lock) .
ล่าม Ruby ทางเลือก เช่น Jruby หรือ Rubinius ใช้ประโยชน์จากมัลติเธรดอย่างเต็มที่
แล้วเธรดคืออะไร
เธรดเป็นผู้ปฏิบัติงานหรือหน่วยของการดำเนินการ
ทุกกระบวนการมีอย่างน้อยหนึ่งเธรด และคุณสามารถสร้างเพิ่มเติมได้ตามต้องการ
ฉันรู้ว่าคุณต้องการดูตัวอย่างโค้ด
แต่ก่อนอื่น เราต้องพูดถึงความแตกต่างระหว่างแอปพลิเคชันที่ผูกกับ CPU และแอปพลิเคชันที่ผูกไว้กับ I/O
แอปพลิเคชันที่ถูกผูกไว้กับ I/O
แอปที่ผูกกับ i/o เป็นสิ่งที่ต้องรอทรัพยากรภายนอก:
- คำขอ API
- ฐานข้อมูล (ผลการสืบค้น)
- อ่านดิสก์
เธรดสามารถตัดสินใจหยุดในขณะที่รอให้ทรัพยากรพร้อมใช้งาน ซึ่งหมายความว่าเธรดอื่นสามารถทำงานได้และไม่เสียเวลารอ
ตัวอย่างหนึ่งของ แอปที่ผูกกับ i/o เป็นโปรแกรมรวบรวมข้อมูลเว็บ
สำหรับทุกคำขอ โปรแกรมรวบรวมข้อมูลต้องรอให้เซิร์ฟเวอร์ตอบสนอง และไม่สามารถทำอะไรได้ในขณะที่รอ
แต่ถ้าคุณใช้เธรด…
คุณสามารถส่งคำขอได้ครั้งละ 4 รายการ &จัดการการตอบกลับเมื่อพวกเขากลับมา ซึ่งจะช่วยให้คุณดึงหน้าได้เร็วขึ้น
ถึงเวลาสำหรับตัวอย่างโค้ดของคุณแล้ว
การสร้างเธรดทับทิม
คุณสามารถสร้างเธรด Ruby ใหม่ได้โดยเรียก Thread.new
.
ตรวจสอบให้แน่ใจว่าได้ส่งผ่านบล็อกด้วยรหัสที่เธรดนี้จำเป็นต้องใช้
Thread.new { puts "hello from thread" }
ค่อนข้างง่ายใช่มั้ย
อย่างไรก็ตาม
หากคุณมีรหัสต่อไปนี้ คุณจะสังเกตเห็นว่าไม่มีผลลัพธ์จากเธรด:
t = Thread.new { puts 10**10 } puts "hello"
ปัญหาคือ Ruby ไม่รอให้เธรดเสร็จ
คุณต้องเรียก join
วิธีการในเธรดของคุณเพื่อแก้ไขโค้ดด้านบน:
t = Thread.new { puts 10**10 } puts "hello" t.join
หากคุณต้องการสร้างหลายเธรด คุณสามารถใส่มันในอาร์เรย์ &โทร join
ในทุกกระทู้
ตัวอย่าง :
threads = [] 10.times { threads << Thread.new { puts 1 } } threads.each(&:join)
ในระหว่างการสำรวจเธรด Ruby คุณอาจพบว่าเอกสารมีประโยชน์:
https://ruby-doc.org/core-2.5.0/Thread.html
กระทู้และข้อยกเว้น
หากเกิดข้อยกเว้นภายในเธรด เธรดนั้นจะตายอย่างเงียบ ๆ โดยไม่หยุดโปรแกรมของคุณหรือแสดงข้อความแสดงข้อผิดพลาดใดๆ
นี่คือตัวอย่าง:
Thread.new { raise 'hell' }
เพื่อจุดประสงค์ในการดีบัก คุณอาจต้องการให้โปรแกรมของคุณหยุดทำงานเมื่อมีสิ่งเลวร้ายเกิดขึ้น ในการทำเช่นนั้น คุณสามารถตั้งค่าแฟล็กต่อไปนี้ใน Thread
เป็นจริง:
Thread.abort_on_exception = true
อย่าลืมตั้งค่าสถานะนี้ก่อนที่คุณจะสร้างเธรดของคุณ 🙂
พูลเธรด
สมมติว่าคุณมีรายการที่ต้องดำเนินการหลายร้อยรายการ การเริ่มเธรดสำหรับแต่ละรายการจะทำลายทรัพยากรระบบของคุณ
มันจะมีลักษณะดังนี้:
pages_to_crawl = %w( index about contact ... ) pages_to_crawl.each do |page| Thread.new { puts page } end
หากคุณทำเช่นนี้ คุณจะต้องเปิดใช้การเชื่อมต่อหลายร้อยรายการกับเซิร์ฟเวอร์ ดังนั้นจึงอาจไม่ใช่ความคิดที่ดี
ทางออกหนึ่งคือการใช้เธรดพูล
กลุ่มเธรดช่วยให้คุณควบคุมจำนวนเธรดที่ใช้งานอยู่ได้ตลอดเวลา
คุณสามารถสร้างสระของคุณเองได้ แต่ฉันไม่แนะนำ ในตัวอย่างต่อไปนี้ เรากำลังใช้เซลลูลอยด์เจมทำสิ่งนี้ให้คุณ
หมายเหตุ:ขณะนี้ Celluloid ไม่ได้รับการบำรุงรักษา แต่แนวคิดทั่วไปของพูลผู้ปฏิบัติงานยังคงมีผลบังคับใช้
require 'celluloid' class Worker include Celluloid def process_page(url) puts url end end pages_to_crawl = %w( index about contact products ... ) worker_pool = Worker.pool(size: 5) # If you need to collect the return values check out 'futures' pages_to_crawl.each do |page| worker_pool.process_page(page) end
คราวนี้จะทำงานเพียง 5 เธรด และเมื่อเสร็จสิ้น พวกเขาจะเลือกรายการถัดไป
สภาพการแข่งขันและอันตรายอื่นๆ
ทั้งหมดนี้อาจฟังดูเจ๋งมาก แต่ก่อนที่คุณจะโรยเธรดให้ทั่วโค้ด คุณต้องรู้ว่ามีปัญหาบางอย่างที่เกี่ยวข้องกับโค้ดที่เกิดขึ้นพร้อมกัน
ตัวอย่างเช่น เธรดมีแนวโน้มที่จะเกิดสภาวะการแข่งขัน
สภาพการแข่งขัน คือเมื่อสิ่งต่าง ๆ เกิดขึ้นไม่เป็นระเบียบและยุ่งเหยิง
อีกปัญหาหนึ่งที่สามารถเกิดขึ้นได้คือการชะงักงัน นี่คือเมื่อเธรดหนึ่งมีการเข้าถึงแบบเอกสิทธิ์เฉพาะบุคคล (โดยใช้ระบบล็อคเช่น mutex) กับทรัพยากรบางอย่างและไม่เคยปล่อยมัน ซึ่งทำให้เธรดอื่นไม่สามารถเข้าถึงได้ทั้งหมด
เพื่อหลีกเลี่ยงปัญหาเหล่านี้ วิธีที่ดีที่สุดคือหลีกเลี่ยงเธรดดิบและยึดติดกับอัญมณีที่ดูแลรายละเอียดให้คุณอยู่แล้ว
อัญมณีเกลียวเพิ่มเติม
เราใช้เซลลูลอยด์สำหรับเธรดพูลของเราแล้ว แต่มีอัญมณีที่เน้นการทำงานพร้อมกันอีกมากมายที่คุณควรตรวจสอบ:
- https://github.com/grosser/parallel
- https://github.com/chadrem/workers
- https://github.com/ruby-concurrency/concurrent-ruby
เรียบร้อย หวังว่าคุณจะได้เรียนรู้สิ่งหนึ่งหรือสองอย่างเกี่ยวกับกระทู้ทับทิม !
หากคุณพบว่าบทความนี้มีประโยชน์ โปรด แชร์ กับเพื่อน ๆ ของคุณเพื่อให้พวกเขาได้เรียนรู้เช่นกัน 🙂