0Pricing
Ruby Academy · บทเรียน

เธรด

การทำงานพร้อมกัน

เธรด เป็นบทเรียน Ruby Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Ruby Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Ruby Academy มีบทเรียนทั้งหมด 4 บทเรียน

เธรดคืออะไร

เธรดคือเส้นทางการทำงานที่เป็นอิสระภายในโปรแกรมของคุณ Ruby ช่วยให้คุณทำงานหลายเธรดได้ เพื่อให้งานเหล่านั้นดำเนินไปคาบเกี่ยวกันตามเวลา

  • โปรแกรมหลักทำงานอยู่ในเธรดหลัก
  • คุณสร้างเธรดเพิ่มได้ด้วย Thread.new
  • เธรดใช้หน่วยความจำร่วมกัน จึงสามารถอ่านและเขียนตัวแปรเดียวกันได้

เธรดเหมาะอย่างยิ่งเมื่อโปรแกรมใช้เวลาไปกับการรอ (เครือข่าย ดิสก์ การหยุดพัก) เพราะเธรดหนึ่งสามารถรอ ขณะที่อีกเธรดยังคงทำงานต่อได้

การสร้างเธรด

ส่งบล็อกให้ Thread.new บล็อกนั้นจะทำงานในเธรดใหม่ เรียก join เพื่อรอให้ทำงานเสร็จก่อนที่โปรแกรมจะจบ

t = Thread.new do
  puts "Hello from the new thread"
end

t.join
puts "Main thread done"

เหตุใด join จึงสำคัญ

เมื่อเธรดหลักจบการทำงาน Ruby จะจบการทำงานด้วย และอาจไม่รอเธรดอื่น join จะหยุดผู้เรียกไว้จนกว่าเธรดเป้าหมายจะทำงานเสร็จ

หากไม่มี join เธรดงานอาจไม่มีโอกาสพิมพ์ผลลัพธ์ก่อนที่โปรแกรมจะปิดลง

t = Thread.new do
  sleep 0.1
  puts "Worker finished"
end

puts "Waiting..."
t.join
puts "All done"

หลายเธรดพร้อมกัน

คุณสามารถเริ่มกลุ่มเธรด เก็บเธรดเหล่านั้นไว้ในอาร์เรย์ แล้วเรียก join กับเธรดแต่ละตัว (each) ได้ นี่เป็นรูปแบบที่ใช้กันทั่วไปสำหรับงานรับส่งข้อมูลที่ทำงานคล้ายขนาน

threads = (1..3).map do |n|
  Thread.new do
    puts "Thread number " + n.to_s
  end
end

threads.each(&:join)
puts "Finished all threads"

การใช้ตัวแปรร่วมกัน

เธรดใช้หน่วยความจำร่วมกัน ตัวแปรที่กำหนดไว้ก่อนสร้างเธรดจะมองเห็นได้จากภายในเธรดนั้น วิธีนี้มีประโยชน์มาก แต่ก็อันตรายเช่นกันหากเธรดสองตัวเขียนค่าเดียวกัน

counter = 0

t = Thread.new do
  counter += 1
end

t.join
puts "Counter is " + counter.to_s

ภาวะการแข่งขัน

ภาวะการแข่งขันเกิดขึ้นเมื่อเธรดสลับการอัปเดตข้อมูลที่ใช้ร่วมกัน และผลลัพธ์ขึ้นอยู่กับจังหวะเวลา

  • counter += 1 แท้จริงแล้วประกอบด้วยการอ่าน การบวก และการเขียน
  • เธรดสองตัวอาจอ่านค่าเดิมเดียวกัน แล้วเขียนค่าใหม่เดียวกัน ทำให้การเพิ่มค่าครั้งหนึ่งสูญหายไป

วิธีแก้คือประสานการเข้าถึงด้วยล็อก

มิวเท็กซ์: ล็อก

Mutex (การไม่ให้เข้าถึงพร้อมกัน) รับรองว่าจะมีเธรดเพียงตัวเดียวที่ทำงานในโค้ดที่ป้องกันไว้ในแต่ละครั้ง ให้ครอบส่วนวิกฤตด้วย mutex.synchronize

mutex = Mutex.new
counter = 0

threads = (1..5).map do
  Thread.new do
    mutex.synchronize do
      counter += 1
    end
  end
end

threads.each(&:join)
puts "Counter is " + counter.to_s

การคืนค่า

Thread#value จะรอให้เธรดทำงานเสร็จ แล้วคืนค่าที่บล็อกส่งกลับมา ซึ่งเทียบได้กับการทำ join โดยอัตโนมัติพร้อมกับคืนผลลัพธ์

t = Thread.new do
  2 + 3
end

puts "Result is " + t.value.to_s

ตัวแปรเฉพาะเธรด

เธรดแต่ละตัวสามารถเก็บค่าของตัวเองโดยใช้สัญลักษณ์เป็นคีย์ผ่าน Thread.current[:key] ค่าเหล่านี้เป็นข้อมูลเฉพาะของเธรดนั้นและไม่ถูกใช้ร่วมกัน

t = Thread.new do
  Thread.current[:name] = "worker"
  puts "Inside: " + Thread.current[:name]
end

t.join

การจัดการข้อยกเว้น

หากเธรดทำให้เกิดข้อผิดพลาด ข้อผิดพลาดนั้นจะยังไม่แสดงจนกว่าคุณจะเรียก join หรือ value ซึ่งจะทำให้ข้อผิดพลาดถูกส่งขึ้นมาอีกครั้ง การตั้งค่า Thread.abort_on_exception = true จะทำให้ข้อผิดพลาดทำให้โปรแกรมทั้งหมดหยุดทำงานทันที ซึ่งช่วยในการแก้ไขข้อผิดพลาด

t = Thread.new do
  raise "boom"
end

begin
  t.join
rescue => e
  puts "Caught: " + e.message
end

เธรดช่วยเมื่อใด

เธรดเหมาะมากกับงานที่ผูกกับการรับส่งข้อมูล ซึ่งใช้เวลาไปกับการรอ เธรดช่วยได้น้อยกว่าสำหรับงานที่ผูกกับ CPU เนื่องจาก GVL (เราจะกล่าวถึงในหัวข้อถัดไป)

  • เหมาะ: ดาวน์โหลดที่อยู่เว็บจำนวนมาก อ่านไฟล์จำนวนมาก
  • มีข้อจำกัด: การคำนวณตัวเลขจำนวนมากแบบ heavy บนหลายคอร์

ปกป้องสถานะที่เปลี่ยนแปลงร่วมกันด้วย Mutex เสมอ

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจของคุณเกี่ยวกับเธรด

ทบทวน: เธรด

คุณได้เรียนรู้หลักสำคัญของเธรดใน Ruby ดังนี้

  • Thread.new เริ่มการทำงานแบบสอดประสาน
  • join ใช้รอ ส่วน value ใช้รอและคืนผลลัพธ์
  • เธรดใช้หน่วยความจำร่วมกัน จึงทำให้เกิดภาวะการแข่งขัน
  • Mutex ที่ใช้ร่วมกับ synchronize จะปกป้องสถานะที่ใช้ร่วมกัน
  • เธรดเหมาะอย่างยิ่งกับงานที่ผูกกับการรับส่งข้อมูล

ต่อไปเราจะสำรวจว่าเหตุใดเธรดจึงใช้หลายคอร์ของ CPU ได้ไม่เต็มที่ นั่นคือ GVL

คำถามที่พบบ่อย

บทเรียน “เธรด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เธรด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Ruby Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Ruby Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เธรด”

การทำงานพร้อมกัน คุณปฏิบัติ Ruby Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Ruby Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Ruby Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “เธรด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Ruby Academy นี้ได้ไหม

ได้ บทเรียน Ruby Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เธรด
  2. อธิบาย GVL
  3. ไฟเบอร์
  4. Ractor
← กลับไปที่ Ruby Academy