การใช้งานที่ "ไม่ต้องล็อก" ในปัจจุบันเป็นไปตามรูปแบบเดียวกันเกือบตลอดเวลา:
- อ่านสถานะและทำสำเนา *
- แก้ไขสำเนา *
- ดำเนินการที่เชื่อมต่อกัน
- ลองใหม่หากล้มเหลว
(* ทางเลือก: ขึ้นอยู่กับโครงสร้างข้อมูล / อัลกอริทึม)
บิตสุดท้ายคล้ายกับสปินล็อคอย่างน่าประหลาด ในความเป็นจริงมันเป็นพื้นฐานspinlock :)
ผมเห็นด้วยกับ @nobugz นี้: ค่าใช้จ่ายในการดำเนินงาน Interlocked ใช้ในการล็อคฟรีแบบมัลติเธรดจะถูกครอบงำโดยแคชและหน่วยความจำที่เชื่อมโยงกันงานก็จะต้องดำเนินการ
อย่างไรก็ตามสิ่งที่คุณได้รับจากโครงสร้างข้อมูลที่ "ไม่ต้องล็อก" ก็คือ "การล็อก" ของคุณจะมีการแบ่งส่วนที่ละเอียดมาก ซึ่งจะช่วยลดโอกาสที่เธรดที่ทำงานพร้อมกันสองเธรดเข้าถึง "ล็อก" เดียวกัน (ตำแหน่งหน่วยความจำ)
เคล็ดลับส่วนใหญ่คือคุณไม่มีการล็อกเฉพาะ - แทนที่จะปฏิบัติต่อเช่นองค์ประกอบทั้งหมดในอาร์เรย์หรือโหนดทั้งหมดในรายการที่เชื่อมโยงเป็น "สปินล็อค" คุณอ่านแก้ไขและพยายามอัปเดตหากไม่มีการอัปเดตตั้งแต่การอ่านครั้งล่าสุดของคุณ หากมีคุณลองอีกครั้ง
สิ่งนี้ทำให้การ "ล็อก" ของคุณ (โอ้ขออภัยไม่ได้ล็อก :) เป็นเม็ดเล็ก ๆ โดยไม่ต้องใช้หน่วยความจำหรือทรัพยากรเพิ่มเติม
การทำให้ละเอียดมากขึ้นจะช่วยลดโอกาสในการรอ การทำให้ละเอียดที่สุดเท่าที่จะเป็นไปได้โดยไม่ต้องแนะนำความต้องการทรัพยากรเพิ่มเติมนั้นฟังดูดีใช่ไหม
ส่วนใหญ่ของความสนุก แต่อาจจะมาจากการสร้างความมั่นใจในการโหลด / ร้านสั่งซื้อที่ถูกต้อง
ตรงกันข้ามกับสัญชาตญาณของคน ๆ หนึ่งซีพียูมีอิสระในการจัดลำดับการอ่าน / เขียนหน่วยความจำใหม่ - พวกมันฉลาดมากโดยวิธีนี้: คุณจะมีช่วงเวลาที่ยากลำบากในการสังเกตสิ่งนี้จากเธรดเดียว อย่างไรก็ตามคุณจะพบปัญหาเมื่อคุณเริ่มทำมัลติเธรดบนหลายคอร์ สัญชาตญาณของคุณจะพังทลายลงเนื่องจากคำสั่งอยู่ก่อนหน้าในโค้ดของคุณมันไม่ได้หมายความว่าจะเกิดขึ้นก่อนหน้า ซีพียูสามารถประมวลผลคำสั่งโดยไม่เรียงลำดับ: และโดยเฉพาะอย่างยิ่งพวกเขาต้องการทำเช่นนี้กับคำแนะนำในการเข้าถึงหน่วยความจำเพื่อซ่อนเวลาแฝงของหน่วยความจำหลักและใช้ประโยชน์จากแคชได้ดีขึ้น
ตอนนี้เป็นที่แน่นอนแล้วว่าเมื่อเทียบกับสัญชาตญาณแล้วว่าลำดับของรหัสจะไม่ไหลแบบ "จากบนลงล่าง" แต่จะทำงานราวกับว่าไม่มีลำดับเลย - และอาจเรียกว่า "สนามเด็กเล่นของปีศาจ" ฉันเชื่อว่ามันเป็นไปไม่ได้ที่จะให้คำตอบที่แน่นอนว่าจะมีการสั่งซื้อใหม่ในการโหลด / จัดเก็บอย่างไร แต่หนึ่งมักจะพูดในแง่ของMaysและmightsและกระป๋องและเตรียมความพร้อมสำหรับที่เลวร้ายที่สุด "โอ้ซีพียูอาจจัดลำดับการอ่านใหม่ให้มาก่อนการเขียนดังนั้นจึงเป็นการดีที่สุดที่จะวางกำแพงหน่วยความจำไว้ตรงนี้"
เรื่องที่มีความซับซ้อนด้วยความจริงที่ว่าแม้เหล่านี้Maysและmightsสามารถแตกต่างกันทั่ว CPU สถาปัตยกรรม มันอาจจะเป็นกรณีเช่นว่าบางสิ่งบางอย่างที่มีการรับประกันว่าจะไม่เกิดขึ้นในหนึ่งสถาปัตยกรรม ที่อาจเกิดขึ้นอีก
หากต้องการใช้มัลติเธรดที่ "ไม่ต้องล็อก" คุณต้องเข้าใจโมเดลหน่วยความจำ
การเดินทางรูปแบบหน่วยความจำและการค้ำประกันที่ถูกต้องคือไม่น่ารำคาญ แต่เป็นแสดงให้เห็นถึงเรื่องนี้โดย Intel และ AMD ทำการแก้ไขบางอย่างเพื่อให้เอกสารของMFENCE
ที่ก่อให้เกิดความปั่นป่วนบางขึ้นในหมู่นักพัฒนา JVM ตามที่ปรากฏเอกสารที่นักพัฒนาใช้ตั้งแต่แรกไม่ได้มีความแม่นยำมากนักในตอนแรก
การล็อกใน. NET ส่งผลให้เกิดอุปสรรคด้านความจำโดยปริยายดังนั้นคุณจึงปลอดภัยในการใช้งาน (โดยส่วนใหญ่นั่นคือ ... ดูตัวอย่างเช่นความยิ่งใหญ่ของ Joe Duffy - Brad Abrams - Vance Morrisonในการเริ่มต้นแบบขี้เกียจการล็อกการระเหยและหน่วยความจำ อุปสรรค :) (อย่าลืมติดตามลิงค์ในหน้านั้น)
เป็นโบนัสเพิ่มคุณจะได้รับการแนะนำให้รู้จักกับหน่วยความจำแบบ .NET ในการแสวงหาด้าน :)
นอกจากนี้ยังมี "oldie แต่โกลดี้" จากแวนซ์มอร์ริสัน: อะไรทุก Dev ต้องรู้จักเกี่ยวกับมัลติเธรดปพลิเคชัน
... และแน่นอนตามที่@Ericกล่าวไว้Joe Duffyเป็นผู้อ่านที่ชัดเจนในหัวข้อนี้
STM ที่ดีสามารถเข้าใกล้การล็อกแบบละเอียดได้มากที่สุดเท่าที่จะทำได้และอาจให้ประสิทธิภาพที่ใกล้เคียงหรือเทียบเท่ากับการใช้งานแบบแฮนด์เมด หนึ่งในนั้นคือSTM.NETจากโครงการ DevLabsของ MS
หากคุณไม่ได้เป็นคนคลั่ง .NET เท่านั้นดั๊กเลียได้บางงานที่ยิ่งใหญ่ใน JSR-166
Cliff Clickมีสิ่งที่น่าสนใจเกี่ยวกับตารางแฮชที่ไม่ต้องพึ่งพาการล็อคสตริปเช่นเดียวกับตารางแฮช Java และ. NET พร้อมกันและดูเหมือนจะปรับขนาดได้ดีถึง 750 CPU
ถ้าคุณไม่กลัวที่จะเสี่ยงเข้าไปในดินแดน Linux, บทความต่อไปนี้ให้เข้าใจมากขึ้นใน internals สถาปัตยกรรมหน่วยความจำในปัจจุบันและวิธีการใช้งานร่วมกันแคชเส้นสามารถทำลายประสิทธิภาพการทำงาน: อะไรโปรแกรมเมอร์ทุกคนควรรู้เกี่ยวกับหน่วยความจำ
@ เบ็นแสดงความคิดเห็นมากมายเกี่ยวกับ MPI: ฉันยอมรับอย่างจริงใจว่า MPI อาจเปล่งประกายในบางพื้นที่ โซลูชันที่ใช้ MPI สามารถให้เหตุผลได้ง่ายขึ้นใช้งานง่ายขึ้นและเกิดข้อผิดพลาดน้อยกว่าการใช้งานการล็อกแบบครึ่งอบที่พยายามจะฉลาด (อย่างไรก็ตาม - เป็นเรื่องส่วนตัว - จริงสำหรับโซลูชันที่ใช้ STM ด้วย) ฉันจะพนันได้ว่าการเขียนแอปพลิเคชันแบบกระจายที่เหมาะสมในเช่น Erlang นั้นง่ายกว่าหลายปีตามตัวอย่างที่ประสบความสำเร็จ
อย่างไรก็ตาม MPI มีค่าใช้จ่ายของตัวเองและปัญหาของตัวเองเมื่อมีการทำงานบนระบบมัลติคอร์เดียว เช่นใน Erlang มีปัญหาที่จะแก้ไขรอบการประสานของการตั้งเวลาของกระบวนการและข้อความคิว
นอกจากนี้ที่แกนหลักระบบ MPI มักใช้การจัดตารางเวลาแบบร่วมมือกันN: Mสำหรับ "กระบวนการที่มีน้ำหนักเบา" ตัวอย่างเช่นหมายความว่ามีการสลับบริบทอย่างหลีกเลี่ยงไม่ได้ระหว่างกระบวนการที่มีน้ำหนักเบา เป็นความจริงที่ว่ามันไม่ใช่ "สวิตช์บริบทแบบคลาสสิก" แต่ส่วนใหญ่เป็นการทำงานของพื้นที่ผู้ใช้และสามารถทำได้อย่างรวดเร็ว - อย่างไรก็ตามฉันสงสัยเป็นอย่างยิ่งว่ามันสามารถนำมาใช้ภายใต้20-200 รอบการดำเนินการที่เชื่อมต่อกันได้ การสลับบริบทโหมดผู้ใช้ช้าลงอย่างแน่นอนแม้แต่ในไลบรารี Intel McRT N: M การตั้งเวลาด้วยกระบวนการที่มีน้ำหนักเบาไม่ใช่เรื่องใหม่ LWP อยู่ที่นั่นใน Solaris เป็นเวลานาน พวกเขาถูกทอดทิ้ง มีเส้นใยใน NT ตอนนี้พวกเขาส่วนใหญ่เป็นของที่ระลึก มี "การเปิดใช้งาน" ใน NetBSD พวกเขาถูกทอดทิ้ง Linux มีส่วนร่วมในเรื่องของเธรด N: M ตอนนี้ดูเหมือนจะตายไปบ้างแล้ว
ในบางครั้งก็มีคู่แข่งใหม่ ๆ เช่นMcRT จาก IntelหรือUser-Mode Scheduling ล่าสุดพร้อมกับConCRTจาก Microsoft
ในระดับต่ำสุดพวกเขาทำในสิ่งที่ตัวกำหนดตารางเวลา N: M MPI ทำ Erlang - หรือระบบใด ๆ MPI - อาจได้รับประโยชน์อย่างมากในระบบ SMP โดยการใช้ประโยชน์ใหม่UMS
ฉันเดาว่าคำถามของ OP ไม่ได้เกี่ยวกับข้อดีและข้อโต้แย้งเชิงอัตนัยสำหรับ / ต่อต้านการแก้ปัญหาใด ๆ แต่ถ้าฉันต้องตอบว่าฉันเดาว่ามันขึ้นอยู่กับงาน: สำหรับการสร้างโครงสร้างข้อมูลพื้นฐานระดับต่ำประสิทธิภาพสูงที่รันบน a ระบบเดียวที่มีแกนจำนวนมากไม่ว่าจะเป็นเทคนิคการล็อคต่ำ / "ไม่ล็อค" หรือ STM จะให้ผลลัพธ์ที่ดีที่สุดในแง่ของประสิทธิภาพและอาจเอาชนะโซลูชัน MPI ได้ทุกเมื่อที่มีประสิทธิภาพแม้ว่าจะมีการรีดริ้วรอยข้างต้นออกไป เช่นใน Erlang
สำหรับการสร้างสิ่งที่ซับซ้อนขึ้นในระดับปานกลางที่ทำงานบนระบบเดียวฉันอาจเลือกการล็อคแบบหยาบแบบคลาสสิกหรือหากประสิทธิภาพเป็นเรื่องที่น่ากังวลมาก STM
สำหรับการสร้างระบบแบบกระจายระบบ MPI น่าจะเป็นทางเลือกที่เป็นธรรมชาติ
โปรดทราบว่ามีการใช้งาน MPIสำหรับ. NET ด้วยเช่นกัน (แม้ว่าจะดูเหมือนจะไม่แอ็คทีฟก็ตาม)