Java และ C # ให้ความปลอดภัยของหน่วยความจำโดยการตรวจสอบขอบเขตของอาเรย์และตัวชี้ dereferences
กลไกใดที่สามารถนำไปใช้กับภาษาโปรแกรมเพื่อป้องกันความเป็นไปได้ของสภาพการแข่งขันและการหยุดชะงัก
Java และ C # ให้ความปลอดภัยของหน่วยความจำโดยการตรวจสอบขอบเขตของอาเรย์และตัวชี้ dereferences
กลไกใดที่สามารถนำไปใช้กับภาษาโปรแกรมเพื่อป้องกันความเป็นไปได้ของสภาพการแข่งขันและการหยุดชะงัก
คำตอบ:
การแข่งขันเกิดขึ้นเมื่อคุณมีนามแฝงพร้อมกันของวัตถุและอย่างน้อยหนึ่งนามแฝงกำลังกลายพันธุ์
ดังนั้นเพื่อป้องกันการแข่งขันคุณต้องทำอย่างน้อยหนึ่งเงื่อนไขเหล่านี้ไม่เป็นความจริง
วิธีการที่หลากหลายจัดการกับแง่มุมต่าง ๆ ฟังก์ชั่นการเขียนโปรแกรมเน้นความไม่เปลี่ยนรูปซึ่งจะขจัดความไม่แน่นอน การล็อค / atomics ลบความพร้อมกัน ประเภท Affine ลบนามแฝง (สนิมลบนามแฝงที่ไม่แน่นอน) ดารารุ่นมักจะลบนามแฝง
คุณสามารถ จำกัด วัตถุที่สามารถใช้นามแฝงเพื่อให้ง่ายต่อการตรวจสอบให้แน่ใจว่าได้หลีกเลี่ยงเงื่อนไขข้างต้น นั่นคือสิ่งที่ช่องทางและ / หรือรูปแบบการส่งข้อความเข้ามาคุณไม่สามารถใช้นามแฝงหน่วยความจำโดยพลการได้ โดยทั่วไปแล้วโดยหลีกเลี่ยงพร้อมกันคือล็อคหรืออะตอม
ข้อเสียของกลไกต่าง ๆ เหล่านี้คือพวกเขา จำกัด โปรแกรมที่คุณสามารถเขียน ยิ่งข้อ จำกัด ทื่อมากขึ้นเท่าใดโปรแกรมก็ยิ่งน้อยลงเท่านั้น ดังนั้นจึงไม่มีนามแฝงหรือไม่มีความไม่แน่นอนและง่ายต่อการให้เหตุผล แต่มีข้อ จำกัด มาก
นั่นเป็นสาเหตุที่ทำให้ Rust เกิดความปั่นป่วน เป็นภาษาทางวิศวกรรม (เทียบกับภาษาเชิงวิชาการ) ที่สนับสนุนนามแฝงและความไม่แน่นอน แต่มีการตรวจสอบคอมไพเลอร์ว่าพวกเขาจะไม่เกิดขึ้นพร้อมกัน แม้ว่าจะไม่เหมาะ แต่ก็อนุญาตให้โปรแกรมที่มีขนาดใหญ่กว่าสามารถเขียนได้อย่างปลอดภัยมากกว่ารุ่นก่อน ๆ
Java และ C # ให้ความปลอดภัยของหน่วยความจำโดยการตรวจสอบขอบเขตของอาเรย์และตัวชี้ dereferences
สิ่งสำคัญคือต้องคิดก่อนว่า C # และ Java ทำสิ่งนี้อย่างไร พวกเขาทำได้โดยการแปลงสิ่งที่เป็นไม่ได้กำหนดพฤติกรรมใน C หรือ C ++ ในเรื่องของพฤติกรรมที่กำหนดไว้: ความผิดพลาดของโปรแกรม ศูนย์ข้อมูลและดัชนีอาร์เรย์ไม่ควรถูกตรวจจับในโปรแกรม C # หรือ Java ที่ถูกต้อง พวกเขาไม่ควรเกิดขึ้นตั้งแต่แรกเพราะโปรแกรมไม่ควรมีข้อผิดพลาดนั้น
แต่นั่นคือฉันคิดว่าไม่ใช่สิ่งที่คุณหมายถึงโดยคำถามของคุณ! เราสามารถเขียนรันไทม์ "deadlock safe" ได้อย่างง่ายดายซึ่งจะตรวจสอบเป็นระยะเพื่อดูว่ามีเธรด n เธรดซึ่งกันและกันที่รอกันและยุติโปรแกรมถ้าเกิดขึ้น แต่ฉันไม่คิดว่าจะทำให้คุณพึงพอใจ
กลไกใดที่สามารถนำไปใช้กับภาษาโปรแกรมเพื่อป้องกันความเป็นไปได้ของสภาพการแข่งขันและการหยุดชะงัก
ปัญหาต่อไปที่เราเผชิญกับคำถามของคุณคือ "สภาพการแข่งขัน" ซึ่งแตกต่างจากการหยุดชะงักนั้นยากที่จะตรวจพบ โปรดจำไว้ว่าสิ่งที่เรากำลังหลังจากในหัวข้อความปลอดภัยไม่ได้ถูกกำจัดเผ่าพันธุ์ สิ่งที่เราทำหลังจากแก้ไขโปรแกรมไม่ว่าใครจะชนะการแข่งขัน ! ปัญหาเกี่ยวกับสภาวะการแข่งขันไม่ใช่ว่าเธรดสองตัวกำลังทำงานตามลำดับที่ไม่ได้กำหนดและเราไม่ทราบว่าใครกำลังจะเสร็จสิ้นก่อน ปัญหาเกี่ยวกับสภาพการแข่งขันคือนักพัฒนาลืมว่าคำสั่งของเธรดจบบางอย่างเป็นไปได้และล้มเหลวในการพิจารณาความเป็นไปได้นั้น
ดังนั้นคำถามของคุณโดยทั่วไปจะลดลงถึง "มีวิธีที่ภาษาการเขียนโปรแกรมสามารถมั่นใจได้หรือไม่ว่าโปรแกรมของฉันถูกต้อง" และคำตอบสำหรับคำถามนั้นคือในทางปฏิบัติไม่ใช่
ป่านนี้ฉันได้วิจารณ์คำถามของคุณเท่านั้น ให้ฉันลองเปลี่ยนเกียร์ที่นี่และตอบคำถามของคุณ มีตัวเลือกที่นักออกแบบภาษาสามารถทำเพื่อลดสถานการณ์ที่น่ากลัวที่เราเผชิญกับการใช้มัลติเธรดหรือไม่?
สถานการณ์เลวร้ายจริงๆ! การรับโค้ดที่มีหลายเธรดให้ถูกต้องโดยเฉพาะอย่างยิ่งในสถาปัตยกรรมโมเดลหน่วยความจำที่อ่อนแอนั้นเป็นเรื่องยากมาก เป็นคำแนะนำให้คิดว่าทำไมมันยาก:
ดังนั้นจึงมีวิธีที่ชัดเจนที่นักออกแบบภาษาสามารถทำให้ดีขึ้นได้ ละทิ้งชนะประสิทธิภาพการทำงานของโปรเซสเซอร์ที่ทันสมัย จัดทำโปรแกรมทั้งหมดแม้กระทั่งแบบมัลติเธรดมีโมเดลหน่วยความจำที่แข็งแกร่งมาก สิ่งนี้จะทำให้โปรแกรมหลายเธรดช้าลงหลายเท่าซึ่งทำงานโดยตรงกับเหตุผลที่มีโปรแกรมหลายเธรดในตอนแรก: เพื่อประสิทธิภาพที่ดีขึ้น
แม้จะละทิ้งโมเดลหน่วยความจำแล้วยังมีเหตุผลอื่นที่ทำให้การมัลติเธรดนั้นยาก:
จุดสุดท้ายนั้นมีคำอธิบายเพิ่มเติม โดย "เรียงความได้" ฉันหมายถึงสิ่งต่อไปนี้:
สมมติว่าเราต้องการคำนวณ int ที่ได้รับสองเท่า เราเขียนการดำเนินการคำนวณที่ถูกต้อง:
int F(double x) { correct implementation here }
สมมติว่าเราต้องการคำนวณสตริงที่ได้รับ int:
string G(int y) { correct implementation here }
ตอนนี้ถ้าเราต้องการคำนวณสตริงที่ได้รับสอง:
double d = whatever;
string r = G(F(d));
G และ F อาจประกอบด้วยวิธีการแก้ไขปัญหาที่ซับซ้อนมากขึ้น
แต่ล็อคไม่ได้มีคุณสมบัตินี้เพราะการหยุดชะงัก วิธีการ M1 ที่ถูกต้องที่ใช้การล็อกตามลำดับ L1, L2 และวิธีการ M2 ที่ถูกต้องที่ใช้การล็อกตามลำดับ L2, L1 ไม่สามารถใช้ทั้งสองในโปรแกรมเดียวกันได้โดยไม่ต้องสร้างโปรแกรมที่ไม่ถูกต้อง ล็อคทำขึ้นเพื่อให้คุณไม่สามารถพูดว่า "ทุกวิธีถูกต้องดังนั้นทุกอย่างถูกต้อง"
ดังนั้นเราสามารถทำอะไรได้บ้างในฐานะนักออกแบบภาษา
ก่อนอื่นอย่าไปที่นั่น การควบคุมหลายเธรดในโปรแกรมเดียวเป็นไอเดียที่ไม่ดีและการแชร์หน่วยความจำข้ามเธรดเป็นแนวคิดที่ไม่ดีดังนั้นอย่าใส่ลงในภาษาหรือรันไทม์ตั้งแต่แรก
เห็นได้ชัดว่านี่ไม่ใช่การเริ่มต้น
ให้เราหันมาใส่ใจกับคำถามพื้นฐานมากขึ้น: ทำไมเราถึงมีหลายกระทู้ในตอนแรก มีสองเหตุผลหลักและพวกเขาจะพูดคุยกันในสิ่งเดียวกันบ่อยครั้งแม้ว่าพวกเขาจะแตกต่างกันมาก พวกเขาสับสนเนื่องจากทั้งคู่เกี่ยวกับการจัดการความล่าช้า
ความคิดที่ไม่ดี ใช้อะซิงโครนัสเธรดเดี่ยวแทนผ่าน coroutines แทน C # ทำสิ่งนี้ได้อย่างสวยงาม Java ไม่ค่อยดี แต่นี่เป็นวิธีหลักที่นักออกแบบภาษาปัจจุบันมีส่วนช่วยแก้ไขปัญหาการทำเกลียว ตัวawait
ดำเนินการใน C # (ได้รับแรงบันดาลใจจากเวิร์กโฟลว์แบบอะซิงโครนัส F และงานศิลปะก่อนหน้าอื่น ๆ ) ได้รับการผนวกเข้ากับภาษามากขึ้นเรื่อย ๆ
นักออกแบบภาษาสามารถช่วยด้วยการสร้างคุณสมบัติภาษาที่ทำงานได้ดีกับความเท่าเทียม ลองคิดดูว่า LINQ จะขยายไปสู่ PLINQ อย่างเป็นธรรมชาติได้อย่างไร หากคุณเป็นคนที่สมเหตุสมผลและคุณ จำกัด การดำเนินการ TPL ของคุณไว้ที่การทำงานของ CPU ที่ขนานกันอย่างมากและไม่แชร์หน่วยความจำคุณสามารถชนะรางวัลใหญ่ได้ที่นี่
เราทำอะไรได้อีก
C # ไม่อนุญาตให้คุณรอในการล็อกเพราะเป็นสูตรสำหรับการหยุดชะงัก C # ไม่อนุญาตให้คุณล็อคค่าประเภทเพราะมันเป็นสิ่งที่ผิดเสมอ คุณล็อคกล่องไม่ใช่ค่า C # เตือนคุณหากคุณมีนามแฝงระเหยเพราะนามแฝงไม่ได้กำหนดความหมายได้รับ / ปล่อย มีหลายวิธีที่คอมไพเลอร์สามารถตรวจพบปัญหาที่พบบ่อยและป้องกันไม่ให้
C # และ Java มีข้อผิดพลาดในการออกแบบอย่างมากโดยอนุญาตให้คุณใช้วัตถุอ้างอิงใด ๆ เป็นจอภาพ สิ่งนี้กระตุ้นให้เกิดการปฏิบัติที่ไม่ดีทุกประเภทซึ่งทำให้ยากต่อการติดตามการหยุดชะงักและการป้องกันไม่ให้เกิดการหยุดนิ่ง และมันจะเสียไบต์ในทุกส่วนหัวของวัตถุ การตรวจสอบควรจะต้องได้รับจากระดับการตรวจสอบ
STM เป็นความคิดที่สวยงามและฉันได้เล่นกับการใช้ของเล่นใน Haskell; มันช่วยให้คุณสามารถเขียนโซลูชันที่ถูกต้องได้อย่างงดงามมากขึ้นจากชิ้นส่วนที่ถูกต้องมากกว่าโซลูชันที่ใช้ระบบล็อค อย่างไรก็ตามฉันไม่ทราบรายละเอียดเพียงพอที่จะบอกว่าทำไมมันไม่สามารถทำงานได้อย่างสมบูรณ์ ถามโจดัฟฟี่ครั้งต่อไปที่คุณเห็นเขา
มีงานวิจัยมากมายเกี่ยวกับภาษาแคลคูลัสในกระบวนการและฉันไม่เข้าใจว่าพื้นที่นั้นดีมาก ลองอ่านเอกสารสองสามฉบับด้วยตัวคุณเองและดูว่าคุณมีข้อมูลเชิงลึกหรือไม่
หลังจากที่ฉันทำงานที่ Microsoft บน Roslyn ฉันทำงานที่ Coverity และสิ่งหนึ่งที่ฉันทำคือรับส่วนวิเคราะห์ด้านหน้าโดยใช้ Roslyn จากการมีการวิเคราะห์คำศัพท์วากยสัมพันธ์และความหมายที่จัดทำโดยไมโครซอฟท์อย่างแม่นยำเราจึงสามารถจดจ่อกับการทำงานหนักของการเขียนเครื่องตรวจจับที่พบปัญหามัลติเธรดทั่วไป
เหตุผลพื้นฐานที่ว่าทำไมเรามีเผ่าพันธุ์และการหยุดชะงักและสิ่งต่าง ๆ นั้นเป็นเพราะเรากำลังเขียนโปรแกรมที่บอกว่าจะทำอะไรและมันกลับกลายเป็นว่าเราทุกคนล้วนเขียนโปรแกรมที่จำเป็น คอมพิวเตอร์ทำในสิ่งที่คุณบอกและเราบอกให้ทำสิ่งผิด ภาษาโปรแกรมสมัยใหม่จำนวนมากเกี่ยวกับการเขียนโปรแกรมเชิงประกาศมากขึ้น: พูดว่าผลลัพธ์ที่คุณต้องการและให้คอมไพเลอร์เข้าใจวิธีที่มีประสิทธิภาพปลอดภัยและถูกต้องเพื่อให้ได้ผลลัพธ์นั้น ลองนึกถึง LINQ อีกครั้ง เราต้องการให้คุณพูดfrom c in customers select c.FirstName
ซึ่งเป็นการแสดงออกถึงความตั้งใจ ให้คอมไพเลอร์เข้าใจวิธีการเขียนโค้ด
อัลกอริทึมการเรียนรู้ของเครื่องเป็นวิธีที่ดีกว่าในบางงานมากกว่าอัลกอริธึมที่เขียนด้วยมือถึงแม้ว่าแน่นอนว่ามีการแลกเปลี่ยนหลายอย่างรวมถึงความถูกต้องเวลาที่ใช้ในการฝึกอบรมอคติที่ได้รับการแนะนำ แต่มีความเป็นไปได้ที่งานจำนวนมากที่เรากำลังใช้รหัส "ด้วยมือ" ในไม่ช้าจะสามารถแก้ไขปัญหาที่สร้างโดยเครื่องจักรได้ หากมนุษย์ไม่ได้เขียนรหัสพวกเขาจะไม่เขียนข้อบกพร่อง
ขออภัยที่นั่นเที่ยวไปเล็กน้อย นี่เป็นหัวข้อที่ใหญ่และยากและไม่มีข้อสรุปที่ชัดเจนเกิดขึ้นในชุมชน PL ใน 20 ปีที่ฉันติดตามความคืบหน้าในพื้นที่ปัญหานี้