Semaphore Programming
การจัดจังหวะการทำงานของเทรด (Thread Synchronization)
ความหมายพื้นฐานของคำว่า semaphores คือระบบการให้สัญญาณด้วยการถือธงในท่าต่างๆ ที่เราจะพบเห็นได้ของเจ้าหน้าที่ประจำสถานีรถไฟ (พนักงานชานชลา) เพื่อใช้ในการส่งสัญญาณบอกคนขับรถไฟ ว่าจะสามารถขับรถไฟแต่ละขบวนในการเข้าใช้รางรถไฟนั้นได้หรือไม่ ตัวอย่างเช่น
เมื่อพนักงานชานชลาลดธงสัญญาณต่ำลง จะเป็นการส่งสัญญาณบอกคนขับรถไฟว่าสามารถนำขบวนเข้ามาในรางนี้ได้
แต่ถ้าพนักงานชานชลายกธงสัญญาณขึ้น จะห้ามรถไฟขบวนนั้นเข้ามาในรางนี้

ต่อมาในปี ค.ศ. 1965 ก็ได้มีผู้เชี่ยวชาญทางด้านวิทยาศาสตร์คอมพิวเตอร์ Mr. Edsger Dijkstra ได้นำแนวคิดของ semaphores มาช่วยแก้ไขปัญหาของการประสานงานของกระบวนการ (process synchronization) เนื่องจากภายในระบบปฏิบัติการจะมีโปรเซสเกิดขึ้นอยู่ตลอดเวลาและเกิดขึ้นมากน้อยอยู่เสมอแต่ด้วยข้อจำกัดของทรัพยากรของระบบที่มีจำกัดทำให้ระบบปฏิบัติการจะต้องคอยจัดสรรและควบคุมให้โปรเซสต่างๆ ที่ต้องการเข้าใช้ทรัพยากรภายในระบบสามารถได้ใช้อย่างต่อเนื่องและทั่วถึงให้มากที่สุด
แต่ในบางครั้งโปรเซสที่กำลังทำงานอยู่นั้นอาจกำลังใช้ข้อมูลร่วมกันอยู่ ดังนั้นระบบปฏิบัติการหรือโปรแกรมที่เขียนขึ้นมาจึงจำเป็นจะต้องมีการจัดจังหวะของการทำงานของแต่ละโปรเซสให้เป็นระเบียบและสามารถดำเนินไปอย่างถูกต้อง
โดยนาย Dijkstra ได้เสนอแนวคิดโดยการกำหนดตัวแปรแบบจำนวนเต็ม (integer) ที่เรียกว่า semaphore S ซึ่งสามารถกำหนดค่าเริ่มต้นได้และใช้งานได้โดยผ่านคำสั่ง 2 คำสั่งเท่านั้น คือ down (wait) และ up (signal) โดยคำสั่งทั้งสองนี้มีนิยามดั้งเดิมดังนี้
โดยคำสั่ง down (หรือ wait()) นั้นจะทำการลดค่าที่ละหนึ่ง และคำสั่ง up (หรือ signal) ก็จะเป็นการเพิ่มค่าที่ละหนึ่งเช่นเดียวกัน เมื่อโปรเซสใดก็ตามที่เรียกคำสั่ง wait() แล้วค่า S ณ ตอนนั้นมีค่าน้อยกว่าหรือเท่ากับศูนย์ โปรเซสเหล่านั้นก็จะต้องถูกบล็อคให้รอจนกว่าโปรเซสที่เข้าในเขตวิกฤตจะออกมาแล้วทำการเพิ่มค่า S โดยเรียกคำสั่ง signal()
เพื่อให้เข้าใจได้ง่ายขึ้นเกี่ยวกับ semaphore สามารถเปรียบเทียบได้กับตะกร้าที่มีลูกบอล (S) จำนวนหนึ่งอยู่ โดยเมื่อมีโปรเซสใดต้องการขอเข้าถึงข้อมูลส่วนกลางในเขตวิกฤตจะต้องหยิบลูกบอลหนึ่งลูกจากตะกร้าโดยเมื่อใดที่โปรเซสนั้นเข้าถึงข้อมูลเสร็จสิ้นแล้ว ก็จะออกมาจากเขตวิกฤติแล้วคืนลูกบอลนั้นกลับไปในตะกร้าเช่นเดิม

โดยทั่วไปจะไม่ได้กำหนดว่าค่า semaphores จะต้องไม่เกินเท่าไหร่ แต่จะขึ้นอยู่กับแพลตฟอร์มนั้นๆหรือขึ้นอยู่กับการกำหนดการใช้งานของผู้ใช้เอง ซึ่งการใช้แนวคิดการนับของ semaphores เปรียบได้กับยามคอยเฝ้าดูแลการเข้าใช้งานทรัพยากรส่วนกลางของระบบ เพราะเมื่อมีโปรเซสตัวหนึ่งในฐานะผู้ผลิต (producer) กำลังบันทึกข้อมูลลงในอาเรย์ที่ถูกใช้งานร่วมกัน และมีโปรเซสอื่นที่เข้ามาอ่านข้อมูลออกจากอาเรย์นี้ในฐานะผู้ใช้ (consumer) ดังนั้นเมื่อ producer ได้เขียนข้อมูลลงไปแล้วก็จะเรียก signal เพื่อส่งสัญญาณแจ้งไปยัง consumer ว่าให้เข้ามาอ่านไปได้เลย โดย consumer ก็จะเรียก wait เพื่อเข้าถึงข้อมูลต่อไป ด้วยวิธีการ signal นี้จะทำให้ไม่มี consumer ไหนเข้าถึงข้อมูลโดยที่ภายในอาเรย์ยังไม่ได้มีข้อมูลอยู่
ระบบที่รองรับ semaphore นั้นมีทั้ง System V และ POSIX ซึ่ง POSIX semaphore นั้นถือว่าเป็นตัวที่พัฒนาตามหลังจาก System V semaphore ที่มีอยู่กับระบบปฏิบัติการ UNIX มานาน รวมทั้งอาจจะมีความยุ่งยากในการเรียกใช้งาน ทำให้ POSIX semaphore ได้ถูกพัฒนาให้ดีขึ้นและแก้ไขจุดเสียของ System V semaphore จนทำให้มีขนาดเล็กกระทัดรัด การรียกใช้งานง่าย และมีความทันสมัยรองรับสถาปัตยกรรมใหม่ๆได้ดีกว่ามาก
เมื่ออ้างถึงการใช้ mutex และ semaphore นั้นจะมีส่วนที่แตกต่างกันกล่าวคือ mutex จะสามารถถูกนำไปใช้ได้สำหรับหลายเทรดที่อยู่ภายในโปเซสเดียวกันเท่านั้น แต่ในขณะที่ POSIX semaphore สามารถถูกนำไปใช้ได้ทั้งระหว่างเทรดภายในโปรเซสเดียวกัน และสำหรับการสื่อสารระหว่างโปรเซสได้ด้วยเช่นกัน โดย POSIX semaphore นี้จะมีการกำหนดชื่อ (named) ก็ได้ หรือไม่กำหนดชื่อ (unnamed) ก็ได้ (เฉพาะในกรณีที่เป็นโปรเซสลูกกับโปรแกรมแม่) เช่นเดียวกันกับการใช้ pipes
แสดงตัวอย่างการควบคุม thread จำนวน 2 ตัวให้ไม่มีการแย่งกันเข้าใช้เขตวิกฤติพร้อมกัน
แสดงตัวอย่างการสร้าง thread จำนวน 3 ตัว โดยแต่ละตัวจะเข้าในเขตวิกฤติจำนวน 3 รอบ ซึ่งแต่ละรอบจะทำการสุ่มเวลา (time) แต่ไม่เกิน 30 วินาทีเพื่อทำการหยุดรอด้วยคำสั่ง sleep(time) ตามเวลาที่ได้สุ่มออกมา โดยในโปรแกรมจะกำหนดค่า semaphore เท่ากับ 2 เพื่ออนุญาตให้ thread สามารถเข้ามาในเขตวิกฤติในเวลาเดียวกันได้มากสุดเพียง 2 ตัวเท่านั้น
แสดงตัวอย่างการจำลองสถานการณ์การลงคะแนนเลือกตั้ง (voting) โดยสร้างเทรดจำนวน 8 เทรดแทนผู้มาใช้สิทธิ์เลือกตั้งที่กำลังรอเข้าไปกากบาท ตามจำนวนตู้เลือกตั้ง 3 ตู้ (booth) ดังนั้นจะต้องกำหนดให้ค่า semaphore มีค่าเท่ากับ 3 ซึ่งหมายถึงการอนุญาตให้เทรด (ผู้มาใช้สิทธิ์เลือกตั้ง) เข้าไปกากบาทเลือกที่ตู้เลือกตั้งที่เตรียมไว้ให้ 3 ตู้ได้
แสดงตัวอย่างเมื่อแต่ละโปรเซสมีการใช้ shared memory ร่วมกัน ซึ่งตัว semaphore เองไม่ได้ทำงานข้ามโปรเซสแต่จะทำงานระหว่างเทรดภายในโปรเซสเดียวกัน
ทำการคอมไพล์โปรแกรม โดยไม่ได้ใช้ semaphore ในการควบคุมการเข้าถึง shared memory ซึ่งผลการทำงานของโปรแกรมจะเห็นว่าทั้ง 4 โปรเซส ต่างแย่งการเข้าใช้พื้นที่ร่วมในเวลาเดียวกัน ทำให้ค่าภายใน shared memory แสดงทับซ้อนกันดังแสดงข้างล่าง
เพิ่มเทคนิคของ semaphore เข้าไปโดยการคอมไพล์โปรแกรมใหม่อีกครั้ง และกำหนด (define) ให้ใช้ USE_SHARED_MEMORY เมื่อรันโปรแกรมอีกครั้งจะสังเกตเห็นว่า semaphore สามารถใช้ในการควบคุมจังหวะการเข้าใช้พื้นที่ shared memory ได้ทีละโปรเซส ดังแสดงข้างล่าง
จากตัวอย่างข้างต้นจะเห็นว่าสามารถใช้ semaphore เพื่อช่วยในการจัดการจังหวะการเข้าใช้งาน shared memory ของแต่ละโปรเซสที่เกิดขึ้นจากคำสั่ง fork() ได้ ดังนั้นจะเห็นว่าข้อดีของ semaphores บนกลไกของการจัดการจังหวะการทำงานของโปรเซสต่างๆที่พยายามเข้าถึงทรัพยากรกลาง ซึ่งสามารถนำไปใช้ได้ทั้งกลุ่มโปรเซสที่เกี่ยวข้องกัน (related processes) เช่น ระหว่างโปรเซสแม่และโปรเซสลูกที่ใช้คำสั่ง fork() และกลุ่มโปรเซสที่ไม่เกี่ยวข้องกัน (unrelated processes)
แสดงตัวอย่างของเข้าถึงทรัพยากรกลางของโปรเซสที่เกี่ยวข้องกัน (related processes) โดยโปรเซสแม่จะทำการสร้างโปรเซสลูกด้วยคำสั่ง fork() ดังตัวอย่างข้างล่าง
จากตัวอย่างข้างต้นจะมีส่วนของหน่วยความจำ (ptr) ที่เชื่อมอยู่กับไฟล์ log.txt ซึ่งทั้งโปรเซสแม่และโปรเซสลูกจะต้องเข้าใช้งานร่วมกัน
แสดงตัวอย่างการจัดจังหวะการเข้าใช้งานทรัพยากรระหว่างสองโปรเซสที่ไม่ได้มีความเกี่ยวข้องกัน (unrelated processes) ซึ่งเป็นโปรแกรมที่แยกออกจากอย่างชัดเจน ดังตัวอย่างข้างล่าง
ให้เปิดหน้าต่าง terminal ขึ้นมาแล้วทำการคอมไพล์และเรียกใช้โปรแกรม sem_server เพื่อให้โปรเซสทำการเข้าไปขอเปิดใช้หน่วยความจำและกำหนดกุญแจการเข้าถึงข้อมูลภายใน
จากตัวอย่างโปรแกรมข้างต้น ตัวโปรแกรม sem_server และ sem_client นั้นจะถูกเรียกขึ้นมาแยกออกจากกัน ซึ่งจะมีการใช้ semaphore ในการควบคุมการจัดจังหวะการเข้าใช้ทรัพยากร โดยปกติเมื่อมีการใช้ mutex จะอนุญาตให้โปรเซสเข้าถึงทรัพยากรแบบตามลำดับ (serial access) แตกต่างจากการใช้ semaphore ที่สามารถให้ โปรเซสเข้าถึงทรัพยากรได้แบบคู่ขนานกันได้ (parallel access)
Last updated
Was this helpful?