Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การเรียนรู้พร้อมกัน

การเรียนรู้พร้อมกัน

ผู้ใช้หลายคนจะใช้แอปของคุณพร้อมกัน และคุณต้องการส่งแอปของคุณให้เร็วที่สุด ดังนั้น คุณจะต้องมีวิธีจัดการกับภาวะพร้อมกัน อย่ากลัว! เว็บเซิร์ฟเวอร์ส่วนใหญ่ทำเช่นนี้โดยค่าเริ่มต้นแล้ว แต่เมื่อคุณต้องการปรับขนาด คุณต้องการใช้การทำงานพร้อมกันอย่างมีประสิทธิภาพสูงสุด

การทำงานพร้อมกันประเภทต่างๆ

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

หลายขั้นตอน (ยูนิคอร์น)

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

กระบวนการของผู้ปฏิบัติงานแต่ละคนมีฐานรหัสเต็มในหน่วยความจำ วิธีนี้ทำให้วิธีนี้ใช้หน่วยความจำค่อนข้างมาก และทำให้ปรับขนาดเป็นโครงสร้างพื้นฐานขนาดใหญ่ได้ยาก

สรุปหลายกระบวนการ
กรณีการใช้งาน ตัวอย่างหนึ่งที่ไม่ใช่ทับทิมที่คุณอาจทราบคือเบราว์เซอร์ Chrome ใช้กระบวนการทำงานพร้อมกันหลายกระบวนการเพื่อให้แต่ละแท็บมีกระบวนการของตนเอง อนุญาตให้แท็บเดียวหยุดทำงานโดยไม่ต้องปิดแอปพลิเคชันทั้งหมด ในกรณีดังกล่าว ยังช่วยแยกการหาประโยชน์จากแท็บเดียวได้
ข้อดี ใช้งานง่ายที่สุด
ละเว้นปัญหาด้านความปลอดภัยของเธรด
ผู้ปฏิบัติงานแต่ละคนสามารถหยุดทำงานโดยไม่ทำให้ระบบส่วนที่เหลือเสียหาย
ข้อเสีย แต่ละกระบวนการจะโหลด codebase แบบเต็มในหน่วยความจำ ทำให้ต้องใช้หน่วยความจำมาก
ดังนั้นจึงไม่ปรับขนาดเป็นการเชื่อมต่อพร้อมกันจำนวนมาก

มัลติเธรด (Puma)

โมเดลการทำเธรดนี้อนุญาตให้กระบวนการเดียวจัดการกับคำขอหลายรายการพร้อมกัน โดยเรียกใช้หลายเธรดภายในกระบวนการเดียว

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

ล็อกล่ามทั่วโลก

สิ่งนี้นำเราไปสู่การล็อคล่ามทั่วโลก (GIL) ใน MRI GIL เป็นตัวล็อคการทำงานของรหัส Ruby ทั้งหมด แม้ว่าชุดข้อความของเราจะทำงานพร้อมกัน แต่มีการใช้งานเพียงครั้งละหนึ่งชุดเท่านั้น

IO ทำงานนอก GIL เมื่อคุณเรียกใช้การสืบค้นฐานข้อมูลโดยรอให้ผลลัพธ์กลับมา จะไม่ล็อก กระทู้อื่นจะมีโอกาสทำงานบ้างในระหว่างนี้ หากคุณคำนวณและดำเนินการกับแฮชหรืออาร์เรย์ในเธรดเป็นจำนวนมาก คุณจะใช้คอร์เดียวเท่านั้นหากคุณใช้ MRI ในกรณีส่วนใหญ่ คุณยังต้องการกระบวนการหลายขั้นตอนเพื่อใช้งานเครื่องของคุณอย่างเต็มที่ หรือคุณอาจใช้ Rubinius หรือ jRuby ซึ่งไม่มี GIL

ความปลอดภัยของเธรด

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

สรุปแบบมัลติเธรด
กรณีการใช้งาน นี่คือตัวเลือก "กลางถนน" ใช้สำหรับเว็บแอปพลิเคชันมาตรฐานจำนวนมากซึ่งควรจัดการกับคำขอสั้นๆ จำนวนมาก (เช่น เว็บแอปพลิเคชันที่ไม่ว่าง)
ข้อดี ใช้หน่วยความจำน้อยกว่าหลายกระบวนการ
ข้อเสีย คุณต้องตรวจสอบให้แน่ใจว่าโค้ดของคุณปลอดภัยสำหรับเธรด
หากเธรดทำให้เกิดการขัดข้อง อาจทำให้กระบวนการของคุณหยุดชะงัก
GIL จะล็อกการทำงานทั้งหมดยกเว้น I/O

วนรอบเหตุการณ์ (บาง)

ลูปเหตุการณ์ถูกใช้เมื่อคุณต้องการดำเนินการ I/O พร้อมกันจำนวนมาก ตัวแบบเองไม่ได้บังคับให้ดำเนินการหลายคำขอพร้อมกัน แต่เป็นวิธีที่มีประสิทธิภาพในการจัดการผู้ใช้จำนวนมากพร้อมกันจำนวนมาก

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

loop do
  if event_queue.any?
    handle_event(event_queue.pop)
  else
    sleep 0.1
  end
end

แบบมีภาพประกอบ

ในภาพประกอบนี้ เรากำลังก้าวไปอีกขั้น ตอนนี้วนรอบกิจกรรมเต้นอย่างสวยงามด้วยระบบปฏิบัติการ คิว และหน่วยความจำบางส่วน

ทีละขั้นตอน

  1. ระบบปฏิบัติการจะติดตามความพร้อมใช้งานของเครือข่ายและดิสก์
  2. เมื่อ OS เห็นว่า I/O พร้อมแล้ว ก็จะส่งเหตุการณ์ไปที่คิว
  3. คิวคือรายการเหตุการณ์ที่วนรอบเหตุการณ์ขึ้นบนสุด
  4. วนรอบเหตุการณ์จัดการเหตุการณ์
  5. ใช้หน่วยความจำบางส่วนในการจัดเก็บข้อมูลเมตาเกี่ยวกับการเชื่อมต่อ
  6. มันสามารถส่งเหตุการณ์ใหม่โดยตรงไปยังคิวเหตุการณ์อีกครั้ง ตัวอย่างเช่น ข้อความเพื่อปิดคิวตามเนื้อหาของกิจกรรม
  7. หากต้องการดำเนินการ I/O ระบบจะแจ้ง OS ว่าสนใจในการดำเนินการ I/O ที่เฉพาะเจาะจง ระบบปฏิบัติการจะติดตามเครือข่ายและดิสก์ (ดู [1]) และเพิ่มเหตุการณ์อีกครั้งเมื่อ I/O พร้อมใช้งาน
สรุปการวนรอบเหตุการณ์
กรณีการใช้งาน เมื่อใช้การเชื่อมต่อพร้อมกันจำนวนมากกับผู้ใช้ของคุณ คิดถึงบริการอย่าง Slack การแจ้งเตือนของ Chrome
ข้อดี เกือบไม่มีหน่วยความจำโอเวอร์เฮดต่อการเชื่อมต่อ
ขยายเป็นการเชื่อมต่อแบบขนานจำนวนมาก
ข้อเสีย เป็นโมเดลทางจิตที่เข้าใจยาก
ขนาดแบทช์ต้องเล็กและคาดเดาได้เพื่อหลีกเลี่ยงการสร้างคิว

ควรใช้อันไหนดี?

เราหวังว่าบทความนี้จะช่วยให้คุณเข้าใจโมเดลการทำงานพร้อมกันต่างๆ ได้ดีขึ้น เป็นเรื่องที่ยากกว่าที่จะเข้าใจในฐานะนักพัฒนาซอฟต์แวร์ แต่การทำความเข้าใจหัวข้อดังกล่าวจะทำให้คุณมีเครื่องมือในการทดสอบและใช้การตั้งค่าที่เหมาะสมสำหรับแอปของคุณ

โดยสรุป

  • สำหรับการทำเธรดของแอปส่วนใหญ่นั้นสมเหตุสมผล ดูเหมือนว่าระบบนิเวศของ Ruby/Rails จะ (ช้า) เคลื่อนไหวในลักษณะนี้
  • หากคุณเรียกใช้แอปที่มีการทำงานพร้อมกันสูงกับสตรีมที่ใช้เวลานาน event-loop จะช่วยให้คุณปรับขนาดได้
  • หากคุณไม่มีไซต์ที่มีการเข้าชมสูง หรือคุณคาดหวังให้พนักงานของคุณหยุดทำงาน ให้เลือกใช้หลายกระบวนการแบบเก่าที่ดี

และเป็นไปได้ที่จะเรียกใช้การวนซ้ำเหตุการณ์ ภายในเธรด ภายในการตั้งค่าแบบหลายกระบวนการ ใช่แล้ว คุณสามารถกินสตรอพวาเฟลและกินมันได้เช่นกัน!

หากคุณต้องการอ่านเพิ่มเติมเกี่ยวกับโมเดลการทำงานพร้อมกันเหล่านี้ โปรดอ่านบทความโดยละเอียดเกี่ยวกับลูปแบบหลายกระบวนการ มัลติเธรด และเหตุการณ์