เธรดและสภาพแวดล้อมแบบอะซิงโครนัสนั้นค่อนข้างยุ่งยากในตอนแรก หากไม่มีแบบจำลองทางจิตใจที่ดีในการจัดการปฏิสัมพันธ์ ก็จะเกิดปัญหาได้ง่ายและจบลงด้วยผลลัพธ์ที่ไม่คาดคิด ยิ่งไปกว่านั้น การทดสอบโค้ดอะซิงโครนัสอาจทำได้ยากหากไม่มีเครื่องมือหรือรูปแบบการทดสอบที่เหมาะสม
การคิดถึงเธรดในฐานะบุคคลและวัตถุที่แชร์เป็น 'สิ่งของ' ที่สามารถเป็นเจ้าของได้ ช่วยจัดระเบียบการทำงานของระบบมัลติเธรด ในตอนนี้ เราจะยกตัวอย่างเพื่อเรียนรู้ทั้งหมดเกี่ยวกับการทดสอบโค้ด Ruby แบบอะซิงโครนัส
หากคุณกำลังใช้ Rails หรือ Rack หรือแอปพลิเคชันใดๆ ที่เป็นส่วนหน้าของเว็บเบราว์เซอร์ คุณอยู่ในสภาพแวดล้อมแบบอะซิงโครนัส แร็ค #call
มักจะถูกเรียกแบบอะซิงโครนัสเสมอ ดังนั้นไม่ว่าคุณจะรู้หรือไม่ก็ตาม มีโอกาสดีที่คุณจะใช้คอมโพเนนต์แบบมัลติเธรดอยู่แล้ว
การทดสอบ:ทริกเกอร์ รวบรวม และตรวจสอบ
การทดสอบ API การเรียกกลับแบบอะซิงโครนัสสามารถทำได้แบบซิงโครนัสโดยทำตามรูปแบบสามขั้นตอน ทริกเกอร์ , สะสม และ ตรวจสอบ . ลองนึกดูว่าแต่ละเธรดเป็นวัตถุและสิ่งของที่แยกจากกัน เช่นกัน สิ่งที่สามารถเป็นเจ้าของได้ทีละคนเท่านั้น
เราจะใช้ตัวอย่างของแบทแมนและชุดที่แตกต่างกัน 7 ชุดของเขา เพราะนั่นเป็นตัวอย่างที่ใช้งานได้จริง และเราสามารถเข้าใจถึงความสำคัญของการรู้ว่าชุดทั้งหมดอยู่กับอัลเฟรดที่จะล้างหรือไม่เมื่อคุณกำลังจะหมดและกอบกู้เมือง
ตัวอย่าง:วันซักผ้าที่ถ้ำค้างคาว
ตัวอย่างคืออัลเฟรดกำลังซักชุดแบทแมน SuitWashScheduler เป็นตัวกำหนดตารางเวลาที่เรียกใช้การโทรกลับสำหรับการซักแต่ละครั้ง ตัวกำหนดตารางเวลาจะทำการโทรกลับเจ็ดครั้งในช่วงเวลาหนึ่งวินาทีโดยเริ่มตั้งแต่หนึ่งวินาทีหลังจากเริ่มต้น ทริกเกอร์คือการสร้าง SuitWashScheduler
.
class SuitWashScheduler
def initialize(cnt)
Thread.new {
cnt.times {
sleep(1.0)
yield
}
}
end
end
การสะสม
การรวบรวมผลจะต้องเป็นเธรดที่ปลอดภัยเพื่อหลีกเลี่ยงสภาวะการแข่งขัน อ็อบเจ็กต์ใดๆ ที่แชร์ข้ามเธรดมากกว่าหนึ่งรายการจะต้องได้รับการปกป้อง การป้องกันเป็นวิธีการติดตามเจ้าของวัตถุ มีเพียงเจ้าของเท่านั้นที่สามารถเปลี่ยนแปลงหรือดูวัตถุได้ ชุดสูทสามารถใช้ได้เฉพาะกับแบทแมนเพื่อใช้ในการต่อสู้ หรือให้ล้างด้วยอัลเฟรด
เพื่อรักษาเธรดที่เป็นมิตร (ในอุปมาแบทแมนหรืออัลเฟรด) จะเป็นเจ้าของเพียงช่วงเวลาสั้น ๆ แล้วละทิ้งความเป็นเจ้าของ A Mutex
มักใช้เพื่อติดตามเจ้าของ SuitwashScheduler
การโทรกลับจะเป็นเจ้าของตัวนับผลลัพธ์เมื่อตัวนับเพิ่มขึ้น การเรียกกลับที่ทำงานใน SuitWashScheduler
เธรดส่งสัญญาณว่าได้รับผลลัพธ์ทั้งหมดแล้วเมื่อตัวนับกระทบกับเป้าหมาย
การเขียนตัวอย่างเริ่มต้นด้วยการตั้งค่าโกลบอลบางส่วน ในการใช้งานจริง globals จะถูกแทนที่ด้วย class หรือ object แอตทริบิวต์
$main_thread = Thread.current
$mu = Mutex.new
$count = 0
$target = 7
ผู้บริหารและเจ้าของ
$main_thread
และ $mu
ใช้เพื่อจัดการเธรดและรอให้การทดสอบเสร็จสิ้นในขณะที่ $target
และ $count
ติดตามผลการทดสอบ จำไว้ว่านี่เป็นการทดสอบเล็กน้อย ดังนั้นการรวบรวมและตรวจสอบผลลัพธ์จะต้องง่าย
การทดสอบเริ่มต้นด้วยการสร้างอินสแตนซ์ใหม่ของ SuitWashScheduler
ให้ตัวเริ่มต้น $target
จำนวนการทำซ้ำ ในกรณีนี้ ชุด 7 ชุดที่ต้องซัก บล็อกที่ให้มาจะทำงานใน SuitWashScheduler
เกลียว. สำหรับการทำซ้ำแต่ละครั้ง $count
เพิ่มขึ้นและพิมพ์
เมื่อมองไปข้างหน้า เราตระหนักดีว่าเธรดทดสอบหลักกำลังตรวจสอบ$count
ซึ่งหมายความว่าจะต้องเป็นเจ้าของ $count
เช่นเดียวกับวิธีการเป็นเจ้าของ $count
มันจำเป็น. $mu
Mutex
อินสแตนซ์คือโทเค็นการเป็นเจ้าของ ในบล็อกที่ส่งผ่านไปยัง SuitWashScheduler.new
เรียก $mu.synchronize
block ใช้เวลาความเป็นเจ้าของนานพอที่จะตั้งค่า $count
และตรวจสอบผลลัพธ์ ตรวจสอบผลลัพธ์เพิ่มเติมในอีกสักครู่
SuitWashScheduler.new($target) {
$mu.synchronize {
$count += 1
puts $count
$main_thread.wakeup if $target <= $count
}
}
ตรวจสอบ:ครบชุดหรือยัง
กลับมาที่หัวข้อหลัก เราต้องรอให้การทดสอบเสร็จสิ้น แบทแมนต้องรอก่อนครบทั้ง 7 ชุด มีสองเงื่อนไขในการตรวจสอบ; การทดสอบจะอัปเดต $count
ตามที่คาดไว้ มิฉะนั้น Batman จะเบื่อกับการรอการทดสอบให้เสร็จและหมดเวลา ก่อนตรวจสอบ $count
เพื่อดูว่ามันถึง $target
. หรือไม่ , ความเป็นเจ้าของ $count
มันจำเป็น. เช่นเดียวกับในบล็อกสำหรับ SuitWashScheduler
เรียก $mu.synchronize
ถูกนำมาใช้
แต่มันไม่ถูกต้อง ถ้าเราล็อกเธรดหลัก SuitWashScheduler
จะทำได้อย่างไร เธรดเคยเปลี่ยน $count
? โชคดีสำหรับเราที่มีเคล็ดลับดีๆ ที่ดูแลเรื่องนี้ Mutex
คลาสมี #sleep
วิธีที่สละสิทธิ์ความเป็นเจ้าของและรอจนกว่าจะหมดเวลาหรือถูกปลุก เมื่อถูกปลุกผ่านระยะหมดเวลาหรือ #wakeup
โทรไปที่กระทู้หลัก $mu
พยายามเข้าครอบครองอีกครั้งก่อนดำเนินการต่อ เมื่อบรรลุความเป็นเจ้าของแล้ว สามารถตรวจสอบผลลัพธ์และกำหนดสถานะผ่านหรือไม่ผ่านของการทดสอบได้
$mu.synchronize {
$mu.sleep($target + 1)
if $target != $count
puts 'FAILED'
else
puts 'Passed! All suits are washed and clean'
end
}
หากคุณต้องการเจาะลึกในเรื่องนี้ คุณสามารถทำให้ตัวอย่างน่าสนใจขึ้นอีกเล็กน้อยโดยพยายามสร้างตัวกำหนดตารางเวลาหลายตัวและดูว่า Mutex รักษา $count
ได้อย่างไร เปลี่ยนจากการชนกัน ราวกับว่าแบทแมนส่งชุดสูทไปให้อัลเฟรดเพื่อซักเสื้อผ้า และชุดอื่นๆ ให้กับร้านซักแห้ง อย่าลืมเปลี่ยนตรรกะเพื่อให้แน่ใจว่า $target
เช็คคือผลรวมของผลตอบแทนที่คาดหวังทั้งหมด
บทสรุป
การทำงานกับเธรดและสภาพแวดล้อมแบบอะซิงโครนัสจะง่ายขึ้นด้วยโมเดลจิตที่เหมาะสม ในโพสต์นี้ เราใช้คนเป็นคำอุปมาสำหรับเธรดและวัตถุทางกายภาพ (ชุด) เป็นคำอุปมาสำหรับออบเจ็กต์ที่ใช้ร่วมกัน ซึ่งสามารถเป็นเจ้าของได้ทีละหนึ่งเธรดหรือทีละคนเท่านั้น . เราคิดว่าวิธีนี้จะทำให้เข้าใจและจดจำได้ง่ายขึ้น
เราหวังว่าตัวอย่างจะทำให้คุณจำกลไกของ async ได้ แต่เราหวังว่าภาพ Batman ที่กำลังจะหมดลงในขณะที่กำลังซักชุดของเขาจะไม่ติดอยู่กับคุณนานเกินไป
ป.ล. หากคุณใช้คำอุปมาเรื่องแบทแมนในบล็อกเสร็จแล้ว โปรดแจ้งให้เราทราบ