มัลติเธรดแบบไม่ต้องล็อคมีไว้สำหรับผู้เชี่ยวชาญด้านเธรดตัวจริง


87

ฉันกำลังอ่านคำตอบที่Jon Skeetให้กับคำถามและในนั้นเขาพูดถึงสิ่งนี้:

เท่าที่ฉันกังวลการมัลติเธรดแบบไม่ต้องล็อคนั้นมีไว้สำหรับผู้เชี่ยวชาญด้านเธรดตัวจริงซึ่งฉันไม่ใช่คนเดียว

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

ดังนั้นคำถามของฉันคือนอกเหนือจากการเรียนรู้ทุกสิ่งที่คุณทำได้เกี่ยวกับเธรด ฯลฯ คุณจะเริ่มพยายามเรียนรู้การเขียนโค้ดมัลติเธรดแบบไม่ล็อคโดยเฉพาะและแหล่งข้อมูลที่ดีมีอะไรบ้าง

ไชโย


ฉันใช้แพลตฟอร์ม gcc, linux และ X86 / X68 การล็อคฟรีนั้นไม่ยากเท่าที่พวกเขาทำให้มันฟัง! ตัวสร้างอะตอม gcc มีอุปสรรคด้านความจำเกี่ยวกับ intel แต่นั่นไม่สำคัญในชีวิตจริง สิ่งที่สำคัญคือหน่วยความจำถูกปรับเปลี่ยนแบบอะตอม มันจะสั่นเมื่อคุณออกแบบโครงสร้างข้อมูลแบบ "ไม่ล็อก" ซึ่งไม่สำคัญว่าเมื่อเธรดอื่นเห็นการเปลี่ยนแปลง รายการที่เชื่อมโยงรายการเดียวข้ามรายการตารางแฮชรายการฟรีและอื่น ๆ ทั้งหมดค่อนข้างง่ายที่จะทำการล็อคฟรี ล็อคฟรีไม่ใช่สำหรับทุกอย่าง เป็นเพียงเครื่องมืออื่นที่เหมาะสำหรับบางสถานการณ์
johnnycrash


การลงคะแนนเพื่อปิดเป็นคำแนะนำทรัพยากรหรือไม่ระบุสิ่งที่คุณต้องการให้ชัดเจน
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


101

การใช้งานที่ "ไม่ต้องล็อก" ในปัจจุบันเป็นไปตามรูปแบบเดียวกันเกือบตลอดเวลา:

  • อ่านสถานะและทำสำเนา *
  • แก้ไขสำเนา *
  • ดำเนินการที่เชื่อมต่อกัน
  • ลองใหม่หากล้มเหลว

(* ทางเลือก: ขึ้นอยู่กับโครงสร้างข้อมูล / อัลกอริทึม)

บิตสุดท้ายคล้ายกับสปินล็อคอย่างน่าประหลาด ในความเป็นจริงมันเป็นพื้นฐาน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 ด้วยเช่นกัน (แม้ว่าจะดูเหมือนจะไม่แอ็คทีฟก็ตาม)


1
แม้ว่าคำตอบนี้จะมีข้อมูลที่ดีมากมาย แต่แนวคิดพาดหัวข่าวที่ว่าอัลกอริทึมและโครงสร้างข้อมูลที่ไม่มีการล็อกนั้นเป็นเพียงการรวบรวมสปินล็อกที่มีเม็ดเล็กละเอียดมากนั้นไม่ถูกต้อง ในขณะที่คุณมักจะเห็นการลองวนซ้ำในโครงสร้างที่ไม่มีการล็อก แต่ลักษณะการทำงานจะแตกต่างกันมาก: การล็อก (รวมถึงสปิล็อก) จะได้รับทรัพยากรบางอย่างโดยเฉพาะและเธรดอื่น ๆ ไม่สามารถดำเนินการได้ในขณะที่ถืออยู่ การ "ลองอีกครั้ง" ในแง่นั้นคือเพียงแค่รอให้ทรัพยากรพิเศษถูกปล่อยออกมา
BeeOnRope

1
ในทางกลับกันอัลกอริทึมที่ไม่มีการล็อกจะไม่ใช้ CAS หรือคำสั่งเกี่ยวกับอะตอมอื่น ๆ เพื่อรับทรัพยากรพิเศษ แต่ควรดำเนินการบางอย่างให้เสร็จสิ้น หากพวกเขาล้มเหลวนั่นเป็นเพราะการแข่งขันแบบละเอียดชั่วขณะกับเธรดอื่นและในกรณีนั้นเธรดอื่นก็ดำเนินการ (ดำเนินการเสร็จสิ้น) หากเธรดถูกสงสัยไปเรื่อย ๆ เธรดอื่น ๆ ทั้งหมดยังคงสามารถดำเนินการต่อได้ สิ่งนี้มีทั้งในเชิงคุณภาพและประสิทธิภาพที่แตกต่างจากล็อคพิเศษ จำนวน "การลองซ้ำ" มักจะต่ำมากสำหรับ CAS-loops ส่วนใหญ่แม้จะมีการโต้แย้งอย่างหนัก
ก็ตาม

1
... แต่แน่นอนว่าไม่ได้หมายความถึงการปรับขนาดที่ดี: การช่วงชิงตำแหน่งหน่วยความจำเดียวมักจะค่อนข้างช้าในเครื่อง SMP เพียงเพราะเวลาแฝงระหว่างซ็อกเก็ตระหว่างคอร์แม้ว่าจำนวน CAS ล้มเหลวก็ตาม ต่ำ.
BeeOnRope

1
@AndrasVass - ฉันเดาว่ามันขึ้นอยู่กับรหัสล็อคแบบ "ดี" กับ "ไม่ดี" ด้วย แน่นอนว่าใคร ๆ ก็สามารถเขียนโครงสร้างและเรียกมันว่าไม่มีการล็อคได้ในขณะที่มันใช้สปิล็อกโหมดผู้ใช้จริงๆและไม่ได้เป็นไปตามข้อกำหนด ฉันขอแนะนำให้ผู้อ่านที่สนใจอ่านบทความนี้จาก Herlihy และ Shavit ซึ่งดูเป็นทางการในหมวดหมู่ต่างๆของอัลกอริธึมที่ใช้ล็อคและไม่ล็อค สิ่งที่ Herlihy ในหัวข้อนี้ขอแนะนำให้อ่าน
BeeOnRope

1
@AndrasVass - ฉันไม่เห็นด้วย โครงสร้างที่ไม่มีการล็อกแบบคลาสสิกส่วนใหญ่ (รายการคิวแผนที่พร้อมกัน ฯลฯ ) ไม่มีการหมุนแม้กระทั่งสำหรับโครงสร้างที่ไม่สามารถใช้ร่วมกันได้และการใช้งานที่มีอยู่ในทางปฏิบัติของสิ่งเดียวกันเช่น Java เป็นไปตามรูปแบบเดียวกัน (ฉันไม่ได้เป็น คุ้นเคยกับสิ่งที่มีอยู่ใน C หรือ C ++ ที่คอมไพล์แบบดั้งเดิมและยากกว่าเนื่องจากไม่มีการรวบรวมขยะ) บางทีคุณและฉันอาจมีคำจำกัดความที่แตกต่างกันของการปั่น: ฉันไม่คิดว่า "CAS-retry" ที่คุณพบใน "การหมุน" แบบไม่ล็อค IMO "หมุน" หมายถึงการรอคอย
BeeOnRope

28

หนังสือของ Joe Duffy:

http://www.bluebytesoftware.com/books/winconc/winconc_book_resources.html

เขายังเขียนบล็อกเกี่ยวกับหัวข้อเหล่านี้

เคล็ดลับในการทำให้โปรแกรมล็อคต่ำถูกต้องคือการทำความเข้าใจในระดับลึกอย่างแม่นยำว่ากฎของโมเดลหน่วยความจำคืออะไรในการผสมผสานระหว่างฮาร์ดแวร์ระบบปฏิบัติการและสภาพแวดล้อมรันไทม์โดยเฉพาะของคุณ

โดยส่วนตัวแล้วฉันไม่ได้อยู่ใกล้สมาร์ทพอที่จะทำการเขียนโปรแกรม low-lock ที่ถูกต้องนอกเหนือจาก InterlockedIncrement แต่ถ้าคุณทำได้ก็เยี่ยมไปเลย ตรวจสอบให้แน่ใจว่าคุณทิ้งเอกสารประกอบจำนวนมากไว้ในโค้ดเพื่อให้คนที่ไม่ฉลาดเท่าคุณไม่เผลอทำลายค่าคงที่ของโมเดลหน่วยความจำของคุณโดยไม่ได้ตั้งใจและแนะนำข้อบกพร่องที่หาไม่ได้


40
ดังนั้นหากทั้งEric LippertและJon Skeetคิดว่าการเขียนโปรแกรมแบบไม่ล็อคมีไว้สำหรับคนที่ฉลาดกว่าตัวเองเท่านั้นฉันก็จะรีบวิ่งหนีเสียงกรีดร้องออกจากความคิดนี้ทันที ;-)
dodgy_coder

20

ทุกวันนี้ไม่มีสิ่งที่เรียกว่า "เธรดแบบล็อกฟรี" มันเป็นสนามเด็กเล่นที่น่าสนใจสำหรับนักวิชาการและในทำนองเดียวกันย้อนกลับไปในช่วงปลายศตวรรษที่แล้วเมื่อฮาร์ดแวร์คอมพิวเตอร์ทำงานช้าและมีราคาแพง อัลกอริทึมของ Dekkerเป็นที่ชื่นชอบเสมอมาฮาร์ดแวร์สมัยใหม่ได้นำมันออกสู่ทุ่งหญ้า มันไม่ทำงานอีกต่อไป

การพัฒนาสองประการได้ยุติสิ่งนี้: ความเหลื่อมล้ำที่เพิ่มขึ้นระหว่างความเร็วของ RAM และ CPU และความสามารถของผู้ผลิตชิปในการใส่แกน CPU มากกว่าหนึ่งตัวบนชิป

ปัญหาความเร็วของ RAM ทำให้นักออกแบบชิปต้องใส่บัฟเฟอร์บนชิป CPU บัฟเฟอร์เก็บรหัสและข้อมูลซึ่งเข้าถึงได้อย่างรวดเร็วโดยแกน CPU และสามารถอ่านและเขียนจาก / ไปยัง RAM ในอัตราที่ช้าลงมาก บัฟเฟอร์นี้เรียกว่าแคชของซีพียูซีพียูส่วนใหญ่มีอย่างน้อยสองตัว แคชระดับที่ 1 มีขนาดเล็กและเร็วส่วนที่ 2 มีขนาดใหญ่และช้ากว่า ตราบใดที่ CPU สามารถอ่านข้อมูลและคำสั่งจากแคชระดับที่ 1 ได้ก็จะทำงานได้อย่างรวดเร็ว การพลาดแคชมีราคาแพงมากทำให้ CPU เข้าสู่โหมดสลีปได้มากถึง 10 รอบหากข้อมูลไม่อยู่ในแคชที่ 1 มากถึง 200 รอบหากไม่ได้อยู่ในแคชที่ 2 และจำเป็นต้องอ่านจาก แกะ.

แกน CPU ทุกตัวมีแคชของตัวเองพวกเขาเก็บ "มุมมอง" ของ RAM ของตัวเอง เมื่อซีพียูเขียนข้อมูลการเขียนจะถูกสร้างไปยังแคชซึ่งจากนั้นจะค่อยๆล้างไปที่ RAM แน่นอนว่าแต่ละคอร์จะมีมุมมองที่แตกต่างกันของเนื้อหา RAM กล่าวอีกนัยหนึ่งซีพียูตัวหนึ่งจะไม่รู้ว่าซีพียูตัวอื่นเขียนอะไรจนกว่าวงจรการเขียน RAM นั้นจะเสร็จสิ้นและ CPU จะรีเฟรชมุมมองของตัวเอง

ที่ไม่เข้ากันอย่างมากกับเธรด คุณจริงๆดูแลสิ่งที่รัฐของด้ายอีกคือเมื่อคุณต้องอ่านข้อมูลที่ถูกเขียนโดยหัวข้ออื่น เพื่อให้มั่นใจว่าคุณต้องตั้งโปรแกรมสิ่งที่เรียกว่าอุปสรรคหน่วยความจำอย่างชัดเจน เป็นแบบดั้งเดิมของ CPU ระดับต่ำที่ช่วยให้แน่ใจว่าแคช CPU ทั้งหมดอยู่ในสถานะที่สอดคล้องกันและมีมุมมองที่ทันสมัยของ RAM การเขียนที่รอดำเนินการทั้งหมดจะต้องล้างไปที่ RAM จากนั้นแคชจะต้องได้รับการรีเฟรช

สิ่งนี้มีอยู่ใน. NET ซึ่งเมธอด Thread.MemoryBarrier () ดำเนินการอย่างใดอย่างหนึ่ง เนื่องจากนี่เป็น 90% ของงานที่คำสั่งล็อคทำ (และ 95 +% ของเวลาดำเนินการ) คุณจึงไม่ล้ำหน้าด้วยการหลีกเลี่ยงเครื่องมือที่. NET ให้คุณและพยายามใช้งานของคุณเอง


2
@ Davy8: องค์ประกอบทำให้ยังคงแข็ง หากฉันมีตารางแฮชที่ไม่มีการล็อกสองตารางและในฐานะผู้บริโภคฉันเข้าถึงทั้งสองตารางนี้จะไม่รับประกันความสอดคล้องกันของสถานะโดยรวม สิ่งที่ใกล้เคียงที่สุดที่คุณสามารถมาได้ในวันนี้คือ STM ซึ่งคุณสามารถใส่การเข้าถึงทั้งสองเช่นในatomicบล็อกเดียว สรุปแล้วการใช้โครงสร้างที่ไม่มีการล็อคอาจเป็นเรื่องยุ่งยากในหลาย ๆ กรณี
Andras Vass

5
ฉันอาจจะผิด แต่ฉันคิดว่าคุณอธิบายผิดว่าการทำงานร่วมกันของแคชทำงานอย่างไร โปรเซสเซอร์มัลติคอร์ที่ทันสมัยส่วนใหญ่มีแคชที่สอดคล้องกันซึ่งหมายความว่าฮาร์ดแวร์แคชจะจัดการเพื่อให้แน่ใจว่ากระบวนการทั้งหมดมีมุมมองเนื้อหา RAM เหมือนกัน - โดยบล็อกการเรียก "อ่าน" จนกว่าการเรียก "เขียน" ที่เกี่ยวข้องทั้งหมดจะเสร็จสิ้น เอกสาร Thread.MemoryBarrier () ( msdn.microsoft.com/en-us/library/… ) ไม่ได้บอกอะไรเกี่ยวกับลักษณะการทำงานของแคชเลย - เป็นเพียงคำสั่งที่ป้องกันไม่ให้โปรเซสเซอร์จัดลำดับการอ่านและเขียนใหม่
Brooks Moses

7
"ทุกวันนี้ไม่มีสิ่งที่เรียกว่า" เธรดแบบล็อกฟรี "" บอกสิ่งนั้นกับโปรแกรมเมอร์ Erlang และ Haskell
Juliet

4
@HansPassant: "ทุกวันนี้ไม่มีสิ่งที่เรียกว่า" เธรดแบบล็อกฟรี " F #, Erlang, Haskell, Cilk, OCaml, Task Parallel Library (TPL) ของ Microsoft และ Threaded Building Blocks (TBB) ของ Intel ล้วนสนับสนุนให้มีการเขียนโปรแกรมแบบมัลติเธรดที่ไม่มีการล็อก วันนี้ฉันไม่ค่อยใช้การล็อกในรหัสการผลิต
JD

6
@HansPassant: "สิ่งกีดขวางหน่วยความจำที่เรียกว่าเป็นแบบดั้งเดิมของ CPU ระดับต่ำที่ทำให้แน่ใจว่าแคช CPU ทั้งหมดอยู่ในสถานะที่สอดคล้องกันและมีมุมมองที่ทันสมัยของ RAM การเขียนที่รอดำเนินการทั้งหมดจะต้องล้างไปที่ RAM จากนั้นจะต้องรีเฟรชแคช " อุปสรรคของหน่วยความจำในบริบทนี้ป้องกันไม่ให้คำสั่งหน่วยความจำ (โหลดและจัดเก็บ) ถูกจัดลำดับใหม่โดยคอมไพเลอร์หรือ CPU ไม่เกี่ยวข้องกับความสอดคล้องของแคช CPU
JD

6

Google เพื่อล็อคโครงสร้างข้อมูลฟรีและหน่วยความจำในการทำธุรกรรมซอฟแวร์

ฉันจะเห็นด้วยกับ John Skeet ในเรื่องนี้ เธรดที่ไม่มีการล็อคเป็นสนามเด็กเล่นของปีศาจและดีที่สุดสำหรับคนที่รู้ว่าพวกเขารู้สิ่งที่พวกเขาต้องรู้


0

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


มีไลบรารีจำนวนมากที่ให้ความหมายของเธรดแบบไม่ต้องล็อก STM มีความน่าสนใจเป็นพิเศษซึ่งมีการใช้งานค่อนข้างมาก
Marcelo Cantos

ฉันเห็นทั้งสองด้านของอันนี้ การได้รับประสิทธิภาพที่มีประสิทธิภาพจากไลบรารีที่ไม่มีการล็อกต้องมีความรู้อย่างลึกซึ้งเกี่ยวกับโมเดลหน่วยความจำ แต่โปรแกรมเมอร์ที่ไม่มีความรู้นั้นก็ยังสามารถได้รับประโยชน์จากข้อได้เปรียบด้านความถูกต้อง
Ben Voigt

0

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.