แรงจูงใจ
เริ่มต้นจากการสัมผัสกับโมเดลการเขียนโปรแกรมแบบมัลติเธรด (กระบวนการ) และสิ่งที่ฉันเรียนรู้คือการซิงโครไนซ์แบบดั้งเดิมที่เกี่ยวข้องกับเซมาฟอร์ (เซมาฟอร์) ฉันไม่รู้ว่าเหตุใดจึงไม่มีสิ่งที่เกี่ยวข้องใน .Net Framework สิ่งที่แย่ก็คือฉันใช้เธอใช้รหัส C++ ที่ผ่านการทดสอบตามเวลาหลายครั้ง เพื่อป้องกันไม่ให้ผู้พลีชีพในการปฏิวัติกินยาและมีเลือดออกโดยเปล่าประโยชน์ ฉันจึงต้องให้กำเนิดตัวเอง
เซมาฟอร์คืออะไร
หากคุณเข้าใจแนวคิดของเซมาฟอร์แล้ว โปรดข้ามส่วนนี้
Semaphore คือสิ่งอำนวยความสะดวกที่ใช้ในสภาพแวดล้อมแบบมัลติเธรด มีหน้าที่ประสานงานเธรดต่างๆ เพื่อให้แน่ใจว่าสามารถใช้ทรัพยากรสาธารณะได้อย่างถูกต้องและสมเหตุสมผล
มาดูกันว่าลานจอดรถทำงานอย่างไร เพื่อความเรียบง่าย สมมติว่าลานจอดรถมีที่จอดรถเพียงสามช่อง และที่จอดรถทั้งสามช่องว่างเปล่าในตอนต้น คือถ้ารถมาพร้อมๆ กัน 5 คัน คนเฝ้าประตูปล่อยให้ 3 คันเข้าไปโดยไม่มีสิ่งกีดขวาง แล้ววางที่กั้นรถลง รถที่เหลือต้องรอที่ทางเข้า และรถคันต่อๆ ไปก็ต้องรอที่ทางเข้าด้วย ทางเข้า. ในเวลานี้ มีรถยนต์คันหนึ่งออกจากลานจอดรถ หลังจากที่นายประตูรู้เรื่องนี้แล้ว เขาก็เปิดประตูรถแล้วนำรถเข้าไป 1 คัน ถ้ามีรถเหลืออีก 2 คัน เขาก็ใส่รถได้อีก 2 คัน และต่อไปเรื่อยๆ
ในระบบลานจอดรถนี้ ที่จอดรถเป็นทรัพยากรสาธารณะ รถแต่ละคันเป็นเหมือนเส้นด้าย และพนักงานเฝ้าประตูทำหน้าที่เป็นสัญญาณ
นอกจากนี้ลักษณะของสัญญาณมีดังนี้: สัญญาณเป็นจำนวนเต็มไม่เป็นลบ (จำนวนลานจอดรถ) และเธรดทั้งหมด (ยานพาหนะ) ที่ผ่านไปจะลดจำนวนเต็มลงหนึ่ง (ผ่านแน่นอนต้องใช้ทรัพยากร ) เมื่อค่าจำนวนเต็มเป็นศูนย์ เธรดทั้งหมดที่พยายามส่งผ่านจะอยู่ในสถานะรอ เรากำหนดการดำเนินการสองประการบนเซมาฟอร์: รอและปล่อย เมื่อเธรดเรียกการดำเนินการรอ เธรดจะผ่านและลดสัญญาณลงทีละหนึ่ง หรือรอจนกว่าสัญญาณจะมีค่ามากกว่าหนึ่งหรือหมดเวลา จริงๆ แล้ว Release จะดำเนินการเพิ่มบนเซมาฟอร์ ซึ่งสอดคล้องกับรถที่ออกจากลานจอดรถ สาเหตุที่การดำเนินการนี้เรียกว่า "ปล่อย" ก็คือการดำเนินการเพิ่มจะปล่อยทรัพยากรที่ได้รับการปกป้องโดยเซมาฟอร์จริงๆ
ทำให้สำเร็จ
ดังที่เราทุกคนทราบกันดีว่าสิ่งอำนวยความสะดวกในการซิงโครไนซ์เธรดที่มีให้ในไลบรารีคลาส .Net Framework ประกอบด้วย:
Monitor, AutoResetEvent, ManualResetEvent, Mutex, ReadWriteLock และ InterLock ในบรรดาสิ่งเหล่านี้ AutoResetEvent, ManualResetEvent และ Mutex นั้นได้มาจาก WaitHandler และจริงๆ แล้วพวกมันห่อหุ้มอ็อบเจ็กต์เคอร์เนลที่ระบบปฏิบัติการจัดเตรียมไว้ให้ ส่วนอื่นๆ ควรมาจากเครื่องเสมือน .Net สิ่งอำนวยความสะดวกจากวัตถุเคอร์เนลระบบปฏิบัติการมีประสิทธิภาพในการใช้งานน้อยกว่าอย่างเห็นได้ชัด อย่างไรก็ตาม ประสิทธิภาพไม่ใช่ปัญหาที่เราต้องพิจารณาที่นี่ เราจะใช้ Monitors สองตัวและวัตถุ ManualResetEvent เพื่อจำลองสัญญาณ
รหัสมีดังนี้:
สัญญาณคลาสสาธารณะ
-
ส่วนตัว ManualResetEvent waitEvent = ใหม่ ManualResetEvent (false);
วัตถุส่วนตัว syncObjWait = วัตถุใหม่ ();
ส่วนตัว int maxCount = 1; // จำนวนทรัพยากรสูงสุด
ส่วนตัว int currentCount = 0; // จำนวนทรัพยากรปัจจุบันของ
สัญญาณสาธารณะ ()
{
}
สัญญาณสาธารณะ (int maxCount)
-
this.maxCount = maxCount;
}
บูลสาธารณะรอ()
-
lock( syncObjWait ) // มีเพียงเธรดเดียวเท่านั้นที่สามารถป้อนโค้ดต่อไปนี้ได้
-
bool waitResult = this.waitEvent.WaitOne(); //จำนวนทรัพยากรที่รออยู่ที่นี่มากกว่าศูนย์
ถ้า (รอผลลัพธ์)
-
ล็อค(นี้)
-
ถ้า (จำนวนปัจจุบัน > 0 )
-
นับปัจจุบัน--;
ถ้า (จำนวนปัจจุบัน == 0)
-
this.waitEvent.Reset();
}
}
อื่น
-
System.Diagnostics.Debug.Assert (เท็จ "สัญญาณไม่อนุญาตให้นับปัจจุบัน < 0" );
-
-
-
รอผลลัพธ์กลับมา;
-
}
/**//// <สรุป>
/// รอการดำเนินการที่อนุญาตให้ส่งคืนการหมดเวลา
/// </สรุป>
/// <ชื่อพารามิเตอร์ = "มิลลิวินาทีหมดเวลา" ></ พารามิเตอร์>
/// <ส่งคืน></ส่งคืน>
บูลสาธารณะรอ (int มิลลิวินาทีหมดเวลา)
-
lock( syncObjWait ) // Monitor ตรวจสอบให้แน่ใจว่าโค้ดคลาสขอบเขตอยู่ภายในส่วนที่สำคัญ
-
bool waitResult = this.waitEvent.WaitOne (มิลลิวินาทีหมดเวลา, เท็จ);
ถ้า (รอผลลัพธ์)
-
ล็อค(นี้)
-
ถ้า (จำนวนปัจจุบัน > 0 )
-
นับปัจจุบัน--;
ถ้า (จำนวนปัจจุบัน == 0)
-
this.waitEvent.Reset();
}
}
อื่น
-
System.Diagnostics.Debug.Assert (เท็จ "สัญญาณไม่อนุญาตให้นับปัจจุบัน < 0" );
-
-
-
รอผลลัพธ์กลับมา;
-
-
การเปิดตัวบูลสาธารณะ ()
-
lock( this ) // Monitor ตรวจสอบให้แน่ใจว่ารหัสคลาสขอบเขตอยู่ภายในส่วนที่สำคัญ
-
นับปัจจุบัน++;
ถ้า( currentCount > this.maxCount )
-
currentCount = นี่.maxCount;
กลับเท็จ;
-
this.waitEvent.Set(); //อนุญาตให้เธรดเรียก Wait เพื่อเข้า
-
กลับเป็นจริง;
}
}