กลับไปหน้าบทความ

อ่าน 8 นาที

ล็อค (วิทยาการคอมพิวเตอร์)

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

ล็อค (วิทยาการคอมพิวเตอร์)

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

ประเภท

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

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

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

โดยทั่วไปแล้ว การล็อกต้องอาศัยการสนับสนุนจากฮาร์ดแวร์เพื่อการใช้งานที่มีประสิทธิภาพ การสนับสนุนนี้มักอยู่ในรูปแบบของ คำสั่ง อะตอมิก อย่างน้อยหนึ่ง คำสั่ง เช่น " test-and-set ", " fetch-and-add " หรือ " compare-and-swap " คำสั่งเหล่านี้ช่วยให้กระบวนการเดียวสามารถทดสอบได้ว่าล็อกว่างหรือไม่ และหากว่าง ก็สามารถล็อกได้ในขั้นตอนอะตอมิกเดียว

สถาปัตยกรรม โปรเซสเซอร์เดี่ยวมีตัวเลือกในการใช้ลำดับคำสั่งที่ไม่สามารถขัดจังหวะได้—โดยใช้คำสั่งพิเศษหรือคำนำหน้าคำสั่งเพื่อปิดใช้งานการขัดจังหวะชั่วคราว—แต่เทคนิคนี้ใช้ไม่ได้กับ เครื่อง มัลติโปรเซสเซอร์ ที่มีหน่วยความจำร่วมกัน การรองรับการล็อกอย่างเหมาะสมในสภาพแวดล้อมมัลติโปรเซสเซอร์อาจต้องใช้ฮาร์ดแวร์หรือซอฟต์แวร์ที่ซับซ้อนมาก พร้อมกับ ปัญหา การซิงโครไนซ์ที่สำคัญ

เหตุผล ที่ต้องใช้ การดำเนินการแบบอะตอมิกก็คือเรื่องการทำงานพร้อมกัน (concurrency) ซึ่งหมายถึงมีงานมากกว่าหนึ่งงานที่ทำงานตรรกะเดียวกัน ตัวอย่างเช่น พิจารณา โค้ด ภาษา C ต่อไปนี้ :

ถ้า( lock == 0 ) { // ถ้า lock ว่าง ให้ตั้งค่า lock = pid ; }

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

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

บางภาษาโปรแกรมรองรับการล็อกด้วยไวยากรณ์ ตัวอย่างในภาษา C#มีดังต่อไปนี้:

เนมสเปซWikipedia.Examples ;การใช้System.Threading ;public class Account // นี่คือตัวตรวจสอบบัญชี{ // ใช้ `object` ในเวอร์ชันก่อน C# 13 private readonly Lock _balanceLock = new (); private decimal _balance = 0 ;public void Deposit ( decimal amount ) { // อนุญาตให้เธรดเพียงเธรดเดียวเท่านั้นที่สามารถเรียกใช้คำสั่งนี้ได้ในแต่ละครั้งlock ( _balanceLock ) { _balance += amount ; } }public void Withdraw ( decimal amount ) { // อนุญาตให้เธรดเพียงเธรดเดียวเท่านั้นที่สามารถเรียกใช้คำสั่งนี้ได้ในแต่ละครั้งlock ( _balanceLock ) { _balance -= amount ; } } }

C# ได้นำSystem.Threading.Lock มาใช้ ใน C# 13 บน.NET 9

รหัสนี้lock(this)อาจก่อให้เกิดปัญหาได้หากสามารถเข้าถึงอินสแตนซ์ได้จากภายนอก[ 1 ]

เช่นเดียวกับJavaภาษา C# ก็สามารถซิงโครไนซ์เมธอดทั้งหมดได้โดยใช้แอตทริบิวต์ MethodImplOptions.Synchronized [ 2 ] [ 3 ]

[MethodImpl(MethodImplOptions.Synchronized)] public void SomeMethod () { // ทำบางอย่าง}

ความละเอียด

ก่อนที่จะทำความเข้าใจเกี่ยวกับระดับความละเอียดของการล็อก (lock granularity) จำเป็นต้องเข้าใจแนวคิดพื้นฐานสามประการเกี่ยวกับการล็อกก่อน:

  • ค่าใช้จ่ายเพิ่มเติมในการใช้ล็อก : ทรัพยากรพิเศษที่ใช้ไปกับการใช้ล็อก เช่น พื้นที่หน่วยความจำที่จัดสรรให้กับล็อก เวลาของ CPU ในการเริ่มต้นและทำลายล็อก และเวลาในการได้มาหรือปล่อยล็อก ยิ่งโปรแกรมใช้ล็อกมากเท่าไร ค่าใช้จ่ายเพิ่มเติมที่เกี่ยวข้องกับการใช้งานก็จะยิ่งมากขึ้นเท่านั้น
  • การแย่งชิงล็อก : ปัญหานี้เกิดขึ้นเมื่อกระบวนการหรือเธรดหนึ่งพยายามขอรับล็อกที่กระบวนการหรือเธรดอื่นถือครองอยู่ ยิ่งล็อกที่มีอยู่มีความละเอียดมากเท่าไหร่ โอกาสที่กระบวนการ/เธรดหนึ่งจะร้องขอล็อกที่อีกกระบวนการ/เธรดหนึ่งถือครองอยู่ก็จะยิ่งน้อยลงเท่านั้น (ตัวอย่างเช่น การล็อกแถวแทนที่จะล็อกทั้งตาราง หรือการล็อกเซลล์แทนที่จะล็อกทั้งแถว)
  • เดดล็อก (Deadlock) : สถานการณ์ที่งานอย่างน้อยสองงานต่างรอการล็อกที่งานอีกงานหนึ่งถือครองอยู่ หากไม่มีการดำเนินการใดๆ งานทั้งสองจะรอไปตลอดกาล

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

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

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

การล็อกฐานข้อมูล

การล็อกฐานข้อมูลสามารถใช้เป็นวิธีการเพื่อให้มั่นใจถึงความสอดคล้องกันของธุรกรรม กล่าวคือ เมื่อทำการประมวลผลธุรกรรมพร้อมกัน (การสลับธุรกรรม) การใช้การล็อกแบบ 2 ขั้นตอนจะช่วยให้การดำเนินการพร้อมกันของธุรกรรมนั้นเทียบเท่ากับการดำเนินการตามลำดับแบบอนุกรม อย่างไรก็ตาม การติดตาย (deadlock) กลายเป็นผลข้างเคียงที่ไม่พึงประสงค์ของการล็อกในฐานข้อมูล การติดตายสามารถป้องกันได้โดยการกำหนดลำดับการล็อกระหว่างธุรกรรมล่วงหน้า หรือตรวจจับได้โดยใช้กราฟการรอคอย ( waits-for graphs ) ทางเลือกอื่นนอกเหนือจากการล็อกเพื่อให้ฐานข้อมูลมีความสอดคล้องกันในขณะที่หลีกเลี่ยงการติดตาย คือการใช้การประทับเวลาทั่วโลกที่มีลำดับสมบูรณ์ (totally ordered global timestamps)

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

  • การล็อกแบบมองโลกในแง่ร้าย : ผู้ใช้ที่อ่านบันทึกโดยมีเจตนาที่จะแก้ไขข้อมูล จะล็อกบันทึกนั้นไว้แต่เพียงผู้เดียวเพื่อป้องกันไม่ให้ผู้ใช้รายอื่นแก้ไขได้ ซึ่งหมายความว่าไม่มีใครสามารถแก้ไขบันทึกนั้นได้จนกว่าผู้ใช้จะปลดล็อก ข้อเสียคือ ผู้ใช้อาจถูกล็อกไม่ให้เข้าถึงข้อมูลเป็นเวลานานมาก ทำให้ระบบตอบสนองช้าลงและก่อให้เกิดความหงุดหงิด
ควรใช้การล็อกแบบมองโลกในแง่ร้ายในกรณีใดบ้าง: วิธีนี้ส่วนใหญ่ใช้ในสภาพแวดล้อมที่มีการแย่งชิงข้อมูล (ปริมาณการร้องขอจากผู้ใช้ไปยังระบบฐานข้อมูลในเวลาเดียวกัน) สูง โดยที่ต้นทุนในการปกป้องข้อมูลผ่านการล็อกนั้นน้อยกว่าต้นทุนในการยกเลิกธุรกรรมหากเกิดข้อขัดแย้งในการทำงานพร้อมกัน การล็อกแบบมองโลกในแง่ร้ายจะเหมาะสมที่สุดเมื่อเวลาในการล็อกสั้น เช่น ในการประมวลผลข้อมูลด้วยโปรแกรม การล็อกแบบมองโลกในแง่ร้ายต้องการการเชื่อมต่อกับฐานข้อมูลอย่างต่อเนื่องและไม่ใช่ตัวเลือกที่ปรับขนาดได้เมื่อผู้ใช้โต้ตอบกับข้อมูล เนื่องจากข้อมูลอาจถูกล็อกเป็นเวลานานพอสมควร จึงไม่เหมาะสมสำหรับการใช้งานในการพัฒนาเว็บแอปพลิเคชัน
  • การล็อกแบบมอง โลกในแง่ดี (Optimistic locking ): วิธีนี้อนุญาตให้ผู้ใช้หลายคนเข้าถึงฐานข้อมูลพร้อมกันได้ ในขณะที่ระบบจะเก็บสำเนาของการอ่านครั้งแรกที่ผู้ใช้แต่ละคนทำไว้ เมื่อผู้ใช้ต้องการอัปเดตเรคอร์ด แอปพลิเคชันจะตรวจสอบว่ามีผู้ใช้รายอื่นเปลี่ยนแปลงเรคอร์ดนั้นหรือไม่นับตั้งแต่การอ่านครั้งล่าสุด แอปพลิเคชันจะทำเช่นนั้นโดยการเปรียบเทียบการอ่านครั้งแรกที่เก็บไว้ในหน่วยความจำกับเรคอร์ดในฐานข้อมูลเพื่อตรวจสอบการเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับเรคอร์ด หากพบความไม่ตรงกันระหว่างการอ่านครั้งแรกกับเรคอร์ดในฐานข้อมูล จะถือเป็นการละเมิดกฎการทำงานพร้อมกัน และทำให้ระบบไม่สนใจคำขออัปเดตใดๆ ระบบจะสร้างข้อความแสดงข้อผิดพลาดและขอให้ผู้ใช้เริ่มกระบวนการอัปเดตใหม่อีกครั้ง วิธีนี้ช่วยปรับปรุงประสิทธิภาพของฐานข้อมูลโดยลดปริมาณการล็อกที่จำเป็นลง ซึ่งจะช่วยลดภาระบนเซิร์ฟเวอร์ฐานข้อมูล วิธีนี้ทำงานได้อย่างมีประสิทธิภาพกับตารางที่ต้องการการอัปเดตจำนวนจำกัด เนื่องจากไม่มีผู้ใช้คนใดถูกล็อก อย่างไรก็ตาม การอัปเดตบางอย่างอาจล้มเหลว ข้อเสียคือการอัปเดตล้มเหลวอย่างต่อเนื่องเนื่องจากปริมาณคำขออัปเดตจำนวนมากจากผู้ใช้หลายคนพร้อมกัน ซึ่งอาจทำให้ผู้ใช้รู้สึกหงุดหงิด
ควรใช้การล็อกแบบมองโลกในแง่ดีที่ใด: วิธีนี้เหมาะสมในสภาพแวดล้อมที่มีการแย่งชิงข้อมูลต่ำ หรือเมื่อจำเป็นต้องเข้าถึงข้อมูลแบบอ่านอย่างเดียว การควบคุมการทำงานพร้อมกันแบบมองโลกในแง่ดีถูกนำมาใช้อย่างกว้างขวางใน .NET เพื่อตอบสนองความต้องการของแอปพลิเคชันบนมือถือและแอปพลิเคชันที่ไม่ได้เชื่อมต่อ[ 4 ]ซึ่งการล็อกแถวข้อมูลเป็นเวลานานๆ นั้นเป็นไปไม่ได้ นอกจากนี้ การรักษาการล็อกเรคอร์ดต้องอาศัยการเชื่อมต่ออย่างต่อเนื่องกับเซิร์ฟเวอร์ฐานข้อมูล ซึ่งเป็นไปไม่ได้ในแอปพลิเคชันที่ไม่ได้เชื่อมต่อ

ตารางความเข้ากันได้ของตัวล็อค

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

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

หมายเหตุ:ในสิ่งพิมพ์บางฉบับ รายการในตารางจะถูกทำเครื่องหมายไว้ว่า "เข้ากันได้" หรือ "ไม่เข้ากัน" หรือ "ใช่" หรือ "ไม่ใช่" ตามลำดับ[ 5 ]

ข้อเสีย

การป้องกันทรัพยากรโดยใช้การล็อกและการซิงโครไนซ์เธรด/กระบวนการมีข้อเสียหลายประการ:

  • ปัญหาการแย่งชิงทรัพยากร: บางเธรด/กระบวนการต้องรอจนกว่าล็อก (หรือชุดล็อกทั้งหมด) จะถูกปล่อย หากเธรดใดเธรดหนึ่งที่ถือล็อกอยู่เกิดหยุดทำงาน หยุดชะงัก บล็อก หรือเข้าสู่ลูปไม่สิ้นสุด เธรดอื่นๆ ที่รอล็อกอาจต้องรออย่างไม่มีกำหนดจนกว่าจะปิดและเปิด เครื่องคอมพิวเตอร์ ใหม่
  • ค่าใช้จ่ายเพิ่มเติม: การใช้ล็อกจะเพิ่มค่าใช้จ่ายเพิ่มเติมสำหรับการเข้าถึงทรัพยากรแต่ละครั้ง แม้ว่าโอกาสที่จะเกิดการชนกันจะมีน้อยมากก็ตาม (อย่างไรก็ตาม โอกาสที่จะเกิดการชนกันใดๆ ก็ตามถือเป็นสภาวะการแข่งขัน )
  • การแก้ไขข้อผิดพลาด: ข้อผิดพลาดที่เกี่ยวข้องกับการล็อกนั้นขึ้นอยู่กับเวลา และอาจมีความละเอียดอ่อนมากและยากต่อการจำลองอย่างยิ่ง เช่นการล็อกตาย (deadlock )
  • ความไม่เสถียร: ความสมดุลที่เหมาะสมระหว่างค่าใช้จ่ายในการล็อกและการแย่งชิงการล็อกอาจมีความเฉพาะเจาะจงสำหรับโดเมนปัญหา (แอปพลิเคชัน) และมีความอ่อนไหวต่อการออกแบบ การใช้งาน และแม้แต่การเปลี่ยนแปลงสถาปัตยกรรมระบบระดับต่ำ ความสมดุลเหล่านี้อาจเปลี่ยนแปลงไปตลอดวงจรชีวิตของแอปพลิเคชัน และอาจต้องมีการเปลี่ยนแปลงครั้งใหญ่เพื่อปรับปรุง (ปรับสมดุลใหม่)
  • ความสามารถในการประกอบเข้าด้วยกัน: การล็อกจะสามารถประกอบเข้าด้วยกันได้ (เช่น การจัดการการล็อกหลายรายการพร้อมกันเพื่อลบรายการ X จากตาราง A และแทรก X ลงในตาราง B อย่างเป็นอะตอม) ก็ต่อเมื่อมีซอฟต์แวร์สนับสนุนที่ค่อนข้างซับซ้อน (มีภาระงานสูง) และการเขียนโปรแกรมแอปพลิเคชันต้องปฏิบัติตามข้อกำหนดที่เข้มงวดอย่างสมบูรณ์แบบ
  • การผกผันลำดับความสำคัญ : เธรด/กระบวนการที่มีลำดับความสำคัญต่ำที่ถือล็อกร่วมกันอาจขัดขวางไม่ให้เธรด/กระบวนการที่มีลำดับความสำคัญสูงดำเนินการต่อไปได้การสืบทอดลำดับความสำคัญสามารถใช้เพื่อลดระยะเวลาของการผกผันลำดับความสำคัญโปรโตคอลการจำกัดลำดับความสำคัญสามารถใช้ในระบบโปรเซสเซอร์เดี่ยวเพื่อลดระยะเวลาการผกผันลำดับความสำคัญในกรณีที่เลวร้ายที่สุด รวมถึงป้องกันภาวะติดตาย ได้
  • การรอคิว : เธรดอื่นๆ ทั้งหมดจะต้องรอหากเธรดที่ถือล็อกอยู่ถูกยกเลิกการทำงานเนื่องจากการขัดจังหวะช่วงเวลาหรือข้อผิดพลาดในการเข้าถึงหน่วยความจำ

กลยุทธ์ การควบคุมการทำงานพร้อมกันบางอย่างสามารถหลีกเลี่ยงปัญหาเหล่านี้ได้บางส่วนหรือทั้งหมด ตัวอย่างเช่นการใช้ช่องทาง (funnel)หรือการจัดลำดับโทเค็น (serializing tokens)สามารถหลีกเลี่ยงปัญหาที่ใหญ่ที่สุดได้ นั่นคือ การติดตาย (deadlock) ทางเลือกอื่นนอกเหนือจากการล็อก ได้แก่ วิธี การซิงโครไนซ์แบบไม่บล็อกเช่น เทคนิคการเขียนโปรแกรมแบบไร้ ล็อก (lock-free programming techniques) และหน่วยความจำแบบทำธุรกรรม (transactional memory ) อย่างไรก็ตาม วิธีการทางเลือกเหล่านี้มักต้องการให้กลไกการล็อกจริงถูกนำไปใช้ในระดับพื้นฐานกว่าของซอฟต์แวร์ปฏิบัติการ ดังนั้นจึงอาจช่วยลดภาระ ในระดับ แอปพลิเคชันจากรายละเอียดของการใช้งานล็อกเท่านั้น โดยปัญหาที่กล่าวมาข้างต้นยังคงต้องได้รับการจัดการในระดับที่ต่ำกว่าแอปพลิเคชัน

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

ขาดความสามารถในการประกอบ

ปัญหาใหญ่ที่สุดอย่างหนึ่งของการเขียนโปรแกรมแบบใช้ล็อกคือ "ล็อกไม่สามารถประกอบกันได้ " กล่าวคือ เป็นเรื่องยากที่จะรวมโมดูลแบบใช้ล็อกขนาดเล็กที่ถูกต้องเข้ากับโปรแกรมขนาดใหญ่ที่ถูกต้องเท่าเทียมกันโดยไม่ต้องแก้ไขโมดูลหรืออย่างน้อยก็ต้องรู้เกี่ยวกับกลไกภายในของโมดูลนั้นSimon Peyton Jones (ผู้สนับสนุนหน่วยความจำธุรกรรมซอฟต์แวร์ ) ยกตัวอย่างแอปพลิเคชันธนาคารดังต่อไปนี้: [ 6 ]ออกแบบคลาสAccountที่อนุญาตให้ไคลเอนต์หลายรายพร้อมกันฝากหรือถอนเงินเข้าบัญชี และกำหนดอัลกอริทึมในการโอนเงินจากบัญชีหนึ่งไปยังอีกบัญชีหนึ่ง

วิธีแก้ปัญหาในส่วนแรกโดยใช้กลไกการล็อกมีดังนี้:

คลาส Account: สมาชิก balance: จำนวนเต็ม สมาชิก mutex: ล็อก เมธอดฝาก (n: จำนวนเต็ม) มิวเท็กซ์ล็อก() ยอดคงเหลือ ← ยอดคงเหลือ + n mutex.unlock() เมธอดถอน (n: จำนวนเต็ม) เงินฝาก(−n) 

ส่วนที่สองของปัญหาซับซ้อนกว่ามาก รูทีน การถ่ายโอนที่ถูกต้องสำหรับโปรแกรมแบบลำดับจะเป็นดังนี้

ฟังก์ชันโอนเงิน (จาก: บัญชี, ไปยัง: บัญชี, จำนวนเงิน: จำนวนเต็ม) ถอนเงินจาก (จำนวนเงิน) ฝากเงิน (จำนวน) 

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

ฟังก์ชัน transfer(from: Account, to: Account, amount: Integer) ถ้า from < to // ลำดับการล็อกตามอำเภอใจ จากล็อก() to.lock() อื่น to.lock() จากล็อก() ถอนเงินจาก (จำนวนเงิน) ฝากเงิน (จำนวน) จาก.ปลดล็อก() to.unlock() 

วิธีการนี้จะซับซ้อนมากขึ้นเมื่อมีตัวล็อกหลายตัว และ ฟังก์ชัน การถ่ายโอนจำเป็นต้องทราบข้อมูลเกี่ยวกับตัวล็อกทั้งหมด ดังนั้นจึงไม่สามารถซ่อนตัวล็อกได้

การสนับสนุนด้านภาษา

ภาษาโปรแกรมแต่ละภาษามีระดับการรองรับการซิงโครไนซ์ที่แตกต่างกัน:

  • Adaจัดเตรียมวัตถุที่ได้รับการป้องกันซึ่งมีซับโปรแกรมหรือรายการที่ได้รับการป้องกันที่มองเห็นได้[ 7 ]รวมถึงการนัดพบ[ 8 ]
  • มาตรฐาน ISO/IEC C ให้ อินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน (API) มาตรฐานการยกเว้นร่วมกัน (ล็อค) ตั้งแต่C11ในส่วนหัวพร้อมฟังก์ชันการจัดการมิวเท็กซ์ต่างๆ[ 9 ]<threads.h>
    • มาตรฐานOpenMPได้รับการสนับสนุนจากคอมไพเลอร์บางตัว และอนุญาตให้ระบุส่วนสำคัญ โดยใช้ pragmas ได้
    • API pthread ของ POSIXให้การสนับสนุนการล็อก[ 10 ] C และ C++ สามารถเข้าถึงคุณสมบัติการล็อกของระบบปฏิบัติการดั้งเดิมได้อย่างง่ายดาย
  • มาตรฐาน ISO/IEC C++ ปัจจุบัน รองรับฟังก์ชันการทำงานแบบมัลติเธรดตั้งแต่C++11 เป็นต้น มา โดยเฉพาะอย่างยิ่ง มีคลาสstd::mutexรวมถึงคลาสล็อกต่างๆ เช่นstd::lock_guardและstd::unique_lockในstd::scoped_lockส่วน<mutex>หัว[ 11 ]
    • Visual C++มีsynchronizeแอตทริบิวต์ของเมธอดที่จะซิงโครไนซ์ แต่แอตทริบิวต์นี้เฉพาะเจาะจงกับอ็อบเจ็กต์ COM ใน สถาปัตยกรรม Windowsและคอมไพเลอร์Visual C++ [ 12 ]
  • C#มีlockคีย์เวิร์ดบนเธรดเพื่อให้แน่ใจว่าเธรดนั้นสามารถเข้าถึงทรัพยากรและSystem.Threading.Lockคลาส ได้อย่างเฉพาะเจาะจง [ 13 ]
  • Visual Basic (.NET)มี คีย์เวิร์ดคล้ายกับ คีย์เวิร์ดSyncLockในภาษา C#lock
  • Javaมีคีย์เวิร์ดsynchronizedสำหรับล็อกบล็อกโค้ดเมธอดหรืออ็อบเจ็กต์[ 14 ]และไลบรารีที่มีโครงสร้างข้อมูลที่ปลอดภัยต่อการทำงานพร้อมกัน Java ยังมีอินเทอร์เฟซอีกjava.util.concurrent.locks.Lockด้วย[ 15 ]
  • Objective-Cมีคีย์เวิร์ด@synchronized[ 16 ]เพื่อล็อกบล็อกของโค้ด และยังมีคลาส NSLock [ 17 ] NSRecursiveLock [ 18 ]และ NSConditionLock [ 19 ]พร้อมกับโปรโตคอล NSLocking [ 20 ]สำหรับการล็อกอีกด้วย
  • PHPมีการล็อกไฟล์[ 21 ]รวมถึงMutexคลาสในpthreadsส่วนขยาย[ 22 ]
  • Python มีกลไก mutexระดับต่ำด้วยthreading.Lockคลาส[ 23 ]
  • มาตรฐาน ISO/IEC Fortran (ISO/IEC 1539-1:2010) จัดเตรียมlock_typeประเภทที่ได้มาในโมดูลภายในiso_fortran_envและ คำสั่ง lock/ unlockตั้งแต่Fortran 2008 [ 24 ]
  • Ruby มีออบเจ็กต์ mutexระดับต่ำและไม่มีคีย์เวิร์ด[ 25 ]
  • Rustจัดเตรียมโครงสร้างstd::sync::Mutex<T>[ 26 ] [ 27 ]
  • ภาษาแอสเซมบลี x86มีLOCKคำนำหน้าสำหรับการดำเนินการบางอย่างเพื่อรับประกันความเป็นอะตอมิกของการดำเนินการเหล่านั้น
  • Haskellใช้โครงสร้างข้อมูลที่เปลี่ยนแปลงได้ที่เรียกว่าMVarซึ่งสามารถว่างเปล่าหรือมีค่า โดยทั่วไปจะเป็นการอ้างอิงถึงทรัพยากร เธรดที่ต้องการใช้ทรัพยากรจะ 'รับ' ค่าของ โดยMVarปล่อยให้ว่างเปล่า และส่งกลับเมื่อเสร็จสิ้น การพยายามรับทรัพยากรจาก ที่ว่างเปล่าMVarจะทำให้เธรดถูกบล็อกจนกว่าทรัพยากรจะพร้อมใช้งาน[ 28 ]นอกจากการล็อกแล้วยังมี การใช้งาน หน่วยความจำธุรกรรมซอฟต์แวร์ อีกด้วย [ 29 ]
  • Go มี อ็อบเจ็กต์Mutexระดับต่ำsync.Mutexในแพ็กเกจ sync ของไลบรารีมาตรฐาน [ 30 ]สามารถใช้สำหรับการล็อกบล็อกโค้ดเมธอดหรืออ็อบเจ็กต์ได้

มิวเท็กซ์กับเซมาฟอร์

มิวเท็กซ์ (Mutex)คือกลไกการล็อกที่บางครั้งใช้การใช้งานพื้นฐานเดียวกันกับเซมาฟอร์แบบไบนารี (Binary Semaphore) อย่างไรก็ตาม วิธีการใช้งานนั้นแตกต่างกัน ในขณะที่เซมาฟอร์แบบไบนารีอาจถูกเรียกกันทั่วไปว่ามิวเท็กซ์ แต่จริงๆ แล้วมิวเท็กซ์มีกรณีการใช้งานและคำจำกัดความที่เฉพาะเจาะจงกว่า กล่าวคือ มีเพียงงานที่ล็อกมิวเท็กซ์เท่านั้นที่จะสามารถปลดล็อกได้ ข้อจำกัดนี้มีจุดมุ่งหมายเพื่อจัดการกับปัญหาที่อาจเกิดขึ้นจากการใช้เซมาฟอร์:

  1. การกลับลำดับความสำคัญ : หากมิวเท็กซ์รู้ว่าใครเป็นผู้ล็อกและควรเป็นผู้ปลดล็อก ก็เป็นไปได้ที่จะเพิ่มลำดับความสำคัญของงานนั้นเมื่อใดก็ตามที่งานที่มีลำดับความสำคัญสูงกว่าเริ่มรอใช้งานมิวเท็กซ์
  2. การยุติงานก่อนกำหนด: มิวเท็กซ์อาจให้ความปลอดภัยในการลบได้เช่นกัน โดยที่งานที่ถือมิวเท็กซ์จะไม่สามารถถูกลบโดยไม่ตั้งใจได้ (นี่ก็เป็นต้นทุนเช่นกัน หากมิวเท็กซ์สามารถป้องกันไม่ให้งานถูกเรียกคืนได้ ตัวเก็บขยะจะต้องตรวจสอบมิวเท็กซ์นั้น)
  3. ภาวะติดตายเมื่อสิ้นสุดการทำงาน: หากงานที่ถือครองมิวเท็กซ์สิ้นสุดลงด้วยเหตุผลใดก็ตามระบบปฏิบัติการสามารถปล่อยมิวเท็กซ์และส่งสัญญาณไปยังงานที่รออยู่เกี่ยวกับสภาวะนี้ได้
  4. การติดตายแบบเรียกซ้ำ: งานหนึ่งสามารถล็อกมิวเท็กซ์แบบเรียกซ้ำได้หลายครั้ง ในขณะที่อีกงานหนึ่งปลดล็อกมิวเท็กซ์นั้นจำนวนครั้งเท่ากัน
  5. การปล่อยโดยไม่ตั้งใจ: จะเกิดข้อผิดพลาดเมื่อปล่อย mutex หากงานที่ปล่อยไม่ใช่เจ้าของ mutex นั้น

ดูเพิ่มเติม

  • คู่มือเกี่ยวกับกลไกการล็อกและส่วนวิกฤต
ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Lock_(computer_science)&oldid=1351251683 "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ ล็อค (วิทยาการคอมพิวเตอร์)

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

ประเภท

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

ความละเอียด

ก่อนที่จะทำความเข้าใจเกี่ยวกับระดับความละเอียดของการล็อก (lock granularity) จำเป็นต้องเข้าใจแนวคิดพื้นฐานสามประการเกี่ยวกับการล็อกก่อน:

การล็อกฐานข้อมูล

การล็อกฐานข้อมูล สามารถใช้เป็นวิธีการเพื่อให้มั่นใจถึงความสอดคล้องกันของธุรกรรม กล่าวคือ เมื่อทำการประมวลผลธุรกรรมพร้อมกัน (การสลับธุรกรรม) การใช้ การล็อกแบบ 2 ขั้นตอน จะช่วยให้การดำเนินการพร้อมกันของธุรกรรมนั้นเทียบเท่ากับการดำเนินการตามลำดับแบบอนุกรม...