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

อ่าน 6 นาที

ผันผวน (การเขียนโปรแกรมคอมพิวเตอร์)

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

ผันผวน (การเขียนโปรแกรมคอมพิวเตอร์)

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

ในภาษา C และ C++

ในภาษา C และ C++ volatileนั้น เป็นตัวกำหนดประเภทเช่นเดียวกับconstและเป็นส่วนหนึ่งของประเภท (เช่น ประเภทของตัวแปรหรือฟิลด์)

พฤติกรรมของvolatileคีย์เวิร์ดในภาษา C และ C++ บางครั้งถูกอธิบายในแง่ของการระงับการเพิ่มประสิทธิภาพของคอมไพเลอร์ที่ทำการเพิ่มประสิทธิภาพดังนี้ 1- ไม่ลบvolatileการอ่านและการเขียนที่มีอยู่ 2- ไม่เพิ่มvolatileการอ่านและการเขียนใหม่ และ 3- ไม่จัดลำดับvolatileการอ่านและการเขียนใหม่ อย่างไรก็ตาม คำจำกัดความนี้เป็นเพียงการประมาณการเพื่อประโยชน์ของผู้เรียนใหม่เท่านั้น และไม่ควรนำคำจำกัดความโดยประมาณนี้ไปใช้ในการเขียนโค้ดจริง

ในภาษา C และด้วยเหตุนี้ในภาษา C++ volatileคำหลักนี้มีจุดประสงค์เพื่อ: [ 1 ]

  • อนุญาตให้เข้าถึงอุปกรณ์I/O ที่แมปหน่วยความจำ
  • อนุญาตให้คงค่าต่างๆ ไว้ได้ตลอดlongjmp.
  • อนุญาตให้แชร์ค่าระหว่างตัวจัดการสัญญาณและส่วนที่เหลือของโปรแกรมในvolatilesig_atomic_tรูปแบบอ็อบเจ็กต์

มาตรฐาน C และ C++ อนุญาตให้เขียนโค้ดที่พกพาได้ซึ่งแชร์ค่าต่างๆ ระหว่างlongjmpอ็อบเจ็กต์volatileและมาตรฐานยังอนุญาตให้เขียนโค้ดที่พกพาได้ซึ่งแชร์ค่าระหว่างตัวจัดการสัญญาณและส่วนที่เหลือของโค้ดในvolatilesig_atomic_tอ็อบเจ็กต์ การใช้คีย์เวิร์ด `for` ในภาษา C และ C++ ในลักษณะอื่นใดนั้นvolatileถือว่าไม่สามารถพกพาได้หรือไม่ถูกต้องโดยเนื้อแท้ โดยเฉพาะอย่างยิ่ง การเขียนโค้ดด้วยvolatileคีย์เวิร์ด `for` สำหรับ อุปกรณ์ I/O ที่แมปหน่วยความจำนั้นไม่สามารถพกพาได้โดยเนื้อแท้และต้องอาศัยความรู้เชิงลึกเกี่ยวกับการใช้งาน C/C++ และแพลตฟอร์มเป้าหมายโดยเฉพาะเสมอ

มัลติเธรดดิ้ง

เป็นความเข้าใจผิดทั่วไปที่ว่าvolatileคีย์เวิร์ดมีประโยชน์ในการเขียน โค้ด มัลติเธรด แบบพกพา ในภาษา C และ C++ volatileคีย์เวิร์ดในภาษา C และ C++ ไม่เคยทำหน้าที่เป็นเครื่องมือแบบพกพาที่มีประโยชน์สำหรับสถานการณ์มัลติเธรดใดๆ เลย[ 2 ] [ 3 ] [ 4 ] [ 5 ]แตกต่างจาก ภาษาการเขียนโปรแกรม JavaและC#การดำเนินการกับvolatileตัวแปรในภาษา C และ C++ ไม่ใช่แบบอะตอมิกและการดำเนินการกับvolatileตัวแปรไม่มี การรับประกัน ลำดับหน่วยความจำ ที่เพียงพอ (เช่นตัวกั้นหน่วยความจำ ) คอมไพเลอร์ ลิงเกอร์ และรันไทม์ส่วนใหญ่ในภาษา C และ C++ ไม่ได้ให้การรับประกันลำดับหน่วยความจำที่จำเป็นเพื่อให้volatileคีย์เวิร์ดมีประโยชน์สำหรับสถานการณ์มัลติเธรดใดๆ ก่อนมาตรฐาน C11 และ C++11โปรแกรมเมอร์ถูกบังคับให้พึ่งพาการรับประกันจากการใช้งานและแพลตฟอร์มแต่ละรายการ (เช่น POSIX และ WIN32) เพื่อเขียน โค้ด มัลติเธรดด้วยมาตรฐาน C11 และ C++11 ที่ทันสมัย ​​โปรแกรมเมอร์สามารถเขียน โค้ด มัลติเธรด แบบพกพา โดยใช้โครงสร้างแบบพกพาใหม่ๆ เช่นstd::atomic<T>เทมเพลต[ 6 ]

ตัวอย่างการใช้งาน I/O แบบแมปหน่วยความจำในภาษา C

ในตัวอย่างนี้ โค้ดจะกำหนดค่าที่เก็บไว้ในตัวแปร `null` fooให้เป็น0`null` จากนั้นจะเริ่มตรวจสอบค่าดังกล่าวซ้ำๆ จนกว่าจะเปลี่ยนเป็น255`null`:

static int foo ;void bar ( void ) { foo = 0 ;ในขณะที่( foo != 255 ) {} }

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

void bar_optimized ( void ) { foo = 0 ;ในขณะที่( จริง) {} }

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

เพื่อป้องกันไม่ให้คอมไพเลอร์ทำการปรับแต่งนี้volatileสามารถใช้คีย์เวิร์ดได้ดังนี้:

static volatile int foo ;void bar ( void ) { foo = 0 ;ในขณะที่( foo != 255 ) {} }

คีย์เวิร์ด นี้volatileจะป้องกันไม่ให้คอมไพเลอร์ย้ายคำสั่งอ่านค่าออกนอกลูป ดังนั้นโค้ดจะสังเกตเห็นการเปลี่ยนแปลงที่คาดหวังของตัวแปรfooนั้น

การเปรียบเทียบการปรับให้เหมาะสมในภาษา C

โปรแกรมภาษาซีต่อไปนี้ พร้อมด้วยตัวอย่างโค้ดภาษาแอสเซมบลีที่แนบมาด้วย แสดงให้เห็นว่าคำสำคัญดังกล่าวมีผลต่อผลลัพธ์ของคอมไพเลอร์อย่างไรใน volatileกรณีนี้ คอมไพเลอร์ที่ใช้คือGCC

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

ข้อบกพร่องของคอมไพเลอร์

แตกต่างจากคุณสมบัติภาษาอื่นๆ ของ C และ C++ volatileคำหลักไม่ได้รับการสนับสนุนอย่างดีจากการใช้งาน C/C++ ส่วนใหญ่ แม้แต่สำหรับการใช้งานแบบพกพาตามมาตรฐาน C และ C++ การใช้งาน C/C++ ส่วนใหญ่มีข้อบกพร่องเกี่ยวกับพฤติกรรมของvolatileคำหลัก[ 7 ] [ 8 ]โปรแกรมเมอร์ควรระมัดระวังเป็นอย่างยิ่งเมื่อใช้volatileคำหลักใน C และ C++

ในภาษาจาวา

ใน ภาษาการเขียนโปรแกรม Javaเวอร์ชันสมัยใหม่ทั้งหมด คำสำคัญ นี้volatileให้การรับประกันดังต่อไปนี้:

  • volatileการอ่านและการเขียนเป็นแบบอะตอมิกโดยเฉพาะอย่างยิ่ง การอ่านและการเขียนไปยังlongฟิลด์doubleจะไม่เกิดการฉีกขาด ( การรับประกัน ความเป็นอะตอมิกใช้ได้เฉพาะกับvolatileค่าพื้นฐานหรือvolatileค่าอ้างอิงเท่านั้นไม่ใช่ค่าอ็อบเจ็กต์ใดๆ)
  • มีการจัดลำดับvolatileการอ่านและการเขียนแบบสากลเพียงลำดับเดียว กล่าวคือvolatileการอ่านจะอ่านค่าปัจจุบัน (ไม่ใช่ค่าในอดีตหรืออนาคต) และvolatileการอ่านทั้งหมดจะสอดคล้องกับลำดับvolatileการเขียน แบบสากลเพียงลำดับเดียว
  • volatileการอ่านและการเขียนมี "การได้มา" และ "การปล่อย" ความหมาย ของหน่วยความจำกั้น (ซึ่งในมาตรฐาน Java เรียกว่าhappens-before ) [ 9 ] [ 10 ]กล่าวอีกนัยหนึ่งคือvolatileให้การรับประกันเกี่ยวกับลำดับสัมพัทธ์ของ การอ่าน volatileและการเขียนที่ไม่ใช่volatileการอ่านและการเขียน กล่าวอีกนัยหนึ่งคือvolatileโดยพื้นฐานแล้วให้การรับประกันการมองเห็นหน่วยความจำแบบเดียวกันกับบล็อก synchronized ของ Java (แต่ไม่มี การรับประกัน การกีดกันร่วมกันของบล็อก synchronized )

โดยรวมแล้ว การรับประกันเหล่านี้ทำให้ เกิดโครงสร้าง มัลติเธรดvolatileที่มีประโยชน์ในJavaโดยเฉพาะอย่างยิ่ง อัลกอริทึม การล็อกแบบตรวจสอบสองครั้ง ทั่วไป จะ ทำงาน ได้อย่างถูกต้องในJava [ 11 ]volatile

Java เวอร์ชันแรกๆ

ก่อน Java เวอร์ชัน 5 มาตรฐาน Java ไม่รับประกันลำดับสัมพัทธ์ของ การอ่าน volatileและvolatileการเขียนข้อมูล กล่าวอีกนัยหนึ่งคือvolatileไม่มีกลไกการ ทำงานแบบ "acquire" และ "release" สำหรับการจัดการหน่วยความจำ ซึ่งจำกัดการใช้งานในฐานะโครงสร้าง มัลติเธรด อย่างมาก โดยเฉพาะอย่างยิ่ง อัลกอริทึม การล็อกแบบ double-checkedทั่วไปที่ใช้กับ Java จะทำงานไม่volatileถูกต้อง

ในภาษา C#

ในC#จะvolatileช่วยให้มั่นใจได้ว่าโค้ดที่เข้าถึงฟิลด์จะไม่ได้รับผลกระทบจากการปรับแต่งที่ไม่ปลอดภัยต่อเธรดบางอย่างที่อาจดำเนินการโดยคอมไพเลอร์ CLR หรือฮาร์ดแวร์ เมื่อฟิลด์ถูกทำเครื่องหมายvolatileคอมไพเลอร์จะได้รับคำสั่งให้สร้าง "ตัวกั้นหน่วยความจำ" หรือ "รั้ว" รอบฟิลด์ ซึ่งจะป้องกันการจัดลำดับคำสั่งใหม่หรือการแคชที่เชื่อมโยงกับฟิลด์ เมื่ออ่านvolatileฟิลด์ คอมไพเลอร์จะสร้างacquire-fenceซึ่งป้องกันไม่ให้การอ่านและการเขียนอื่นๆ ไปยังฟิลด์ถูกย้ายก่อนรั้ว เมื่อเขียนไปยังvolatileฟิลด์ คอมไพเลอร์จะสร้างrelease-fenceรั้วนี้จะป้องกันไม่ให้การอ่านและการเขียนอื่นๆ ไปยังฟิลด์ถูกย้ายหลังจากรั้ว[ 12 ]

สามารถทำเครื่องหมายเฉพาะประเภทต่อไปนี้ได้volatile: ประเภทอ้างอิงทั้งหมด, Single, Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Char, และประเภทแจงนับ ทั้งหมด ที่มีประเภทพื้นฐานเป็นByte, SByte, Int16, UInt16, Int32, หรือUInt32[ 13 ] (ไม่รวมโครงสร้าง ค่า รวมถึงประเภทพื้นฐานDouble, Int64, UInt64และDecimal)

การใช้volatileคีย์เวิร์ดไม่รองรับฟิลด์ที่ส่งผ่านโดยการอ้างอิงหรือตัวแปรโลคัลที่ถูกจับในกรณีเหล่านี้Thread.VolatileReadต้องThread.VolatileWriteใช้แทน[ 12 ]

โดยหลักแล้ว วิธีการเหล่านี้จะปิดใช้งานการเพิ่มประสิทธิภาพบางอย่างที่ปกติแล้วดำเนินการโดยคอมไพเลอร์ C# คอมไพเลอร์ JIT หรือ CPU เอง การรับประกันที่ให้โดยThread.VolatileReadและThread.VolatileWriteเป็นซูเปอร์เซ็ตของการรับประกันที่ให้โดยvolatileคีย์เวิร์ด: แทนที่จะสร้าง "รั้วครึ่งเดียว" (เช่น acquire-fence ป้องกันเฉพาะการเรียงลำดับคำสั่งใหม่และการแคชที่อยู่ก่อนหน้านั้น) VolatileReadและVolatileWriteสร้าง "รั้วเต็ม" ซึ่งป้องกันการเรียงลำดับคำสั่งใหม่และการแคชของฟิลด์นั้นในทั้งสองทิศทาง[ 12 ]วิธีการเหล่านี้ทำงานดังต่อไปนี้: [ 14 ]

  • วิธีการ นี้Thread.VolatileWriteบังคับให้เขียนค่าลงในฟิลด์ ณ จุดที่เรียกใช้ฟังก์ชัน นอกจากนี้ การโหลดและจัดเก็บข้อมูลตามลำดับโปรแกรมก่อนหน้านี้จะต้องเกิดขึ้นก่อนการเรียกใช้ฟังก์ชัน และการโหลดVolatileWriteและจัดเก็บข้อมูลตามลำดับโปรแกรมในภายหลังจะต้องเกิดขึ้นหลังจากเรียกใช้ฟังก์ชันแล้ว
  • วิธีการ นี้Thread.VolatileReadบังคับให้ค่าในฟิลด์ถูกอ่านจากจุดที่เรียกใช้ฟังก์ชัน นอกจากนี้ การโหลดและจัดเก็บข้อมูลตามลำดับโปรแกรมก่อนหน้านี้จะต้องเกิดขึ้นก่อนการเรียกใช้ฟังก์ชัน และการโหลดVolatileReadและจัดเก็บข้อมูลตามลำดับโปรแกรมในภายหลังจะต้องเกิดขึ้นหลังจากเรียกใช้ฟังก์ชันแล้ว

เมธอดThread.VolatileReadand Thread.VolatileWriteสร้าง full fence โดยการเรียกThread.MemoryBarrierเมธอด ซึ่งสร้าง memory barrier ที่ทำงานได้ทั้งสองทิศทาง นอกจากแรงจูงใจในการใช้ full fence ที่กล่าวมาข้างต้นแล้ว ปัญหาหนึ่งที่อาจเกิดขึ้นกับvolatileคีย์เวิร์ดที่แก้ไขได้โดยการใช้ full fence ที่สร้างโดยThread.MemoryBarrierคือ: เนื่องจากลักษณะที่ไม่สมมาตรของ half fence volatileฟิลด์ที่มีคำสั่งเขียนตามด้วยคำสั่งอ่านอาจยังคงมีลำดับการทำงานที่สลับกันโดยคอมไพเลอร์ เนื่องจาก full fence มีความสมมาตร จึงไม่ใช่ปัญหาเมื่อใช้Thread.MemoryBarrier. [ 12 ]

ในภาษาฟอร์ทราน

VOLATILEเป็นส่วนหนึ่งของมาตรฐานFortran 2003 [ 15 ]แม้ว่าเวอร์ชันก่อนหน้าจะรองรับเป็นส่วนขยายvolatileก็ตาม การทำให้ตัวแปรทั้งหมดในฟังก์ชันมีประโยชน์ในการค้นหาข้อบกพร่องที่เกี่ยวข้องกับ การตั้งชื่อแทน

จำนวนเต็ม, volatile :: i ! เมื่อไม่ได้กำหนด volatile โค้ดสองบรรทัดต่อไปนี้จะเหมือนกันwrite ( * , * ) i ** 2 ! โหลดตัวแปร i จากหน่วยความจำหนึ่งครั้งและคูณค่าดังกล่าวกับตัวเองwrite ( * , * ) i * i ! โหลดตัวแปร i จากหน่วยความจำสองครั้งและคูณค่าเหล่านั้นเข้าด้วยกัน

ด้วยการ "เจาะลึก" ลงไปในหน่วยความจำของ VOLATILE เสมอ คอมไพเลอร์ Fortran จะไม่สามารถจัดลำดับการอ่านหรือการเขียนไปยัง volatile ใหม่ได้ ซึ่งจะทำให้เธรดอื่นมองเห็นการกระทำที่ทำในเธรดนี้ และในทางกลับกัน[ 16 ]

การใช้ VOLATILE ช่วยลดและอาจขัดขวางการปรับให้เหมาะสมได้[ 17 ]

  • คู่มืออ้างอิง Ada C.6: การควบคุมตัวแปรที่ใช้ร่วมกัน
  • เคอร์เนลลินุกซ์: volatile-considered-harmful
ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Volatile_(computer_programming)&oldid=1324854885 "

สรุปเนื้อหา

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

ข้อมูลสำคัญเกี่ยวกับ ผันผวน (การเขียนโปรแกรมคอมพิวเตอร์)

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

ในภาษา C และ C++

ในภาษา C และ C++ volatile นั้น เป็น ตัวกำหนดประเภท เช่นเดียวกับ const และเป็นส่วนหนึ่งของ ประเภท (เช่น ประเภทของตัวแปรหรือฟิลด์)

มัลติเธรดดิ้ง

เป็นความเข้าใจผิดทั่วไปที่ว่า volatile คีย์เวิร์ดมีประโยชน์ในการเขียน โค้ด มัลติเธรด แบบพกพา ในภาษา C และ C++ volatile คีย์เวิร์ดในภาษา C และ C++ ไม่เคย ทำหน้าที่เป็นเครื่องมือแบบพกพาที่มีประโยชน์สำหรับสถานการณ์มัลติเธรด ใดๆ เลย [ 2 ] [ 3 ] [ 4 ] [ 5 ]...

ตัวอย่างการใช้งาน I/O แบบแมปหน่วยความจำในภาษา C

ในตัวอย่างนี้ โค้ดจะกำหนดค่าที่เก็บไว้ในตัวแปร `null` foo ให้เป็น 0 `null` จากนั้นจะเริ่ม ตรวจสอบ ค่าดังกล่าวซ้ำๆ จนกว่าจะเปลี่ยนเป็น 255 `null`: