มีวิธีปฏิบัติที่เลิกใช้แล้วสำหรับการเขียนโปรแกรมมัลติเธรดและมัลติโปรเซสเซอร์ซึ่งฉันไม่ควรใช้อีกต่อไปหรือไม่?


36

ในวันแรก ๆ ของ FORTRAN และ BASIC โปรแกรมทั้งหมดถูกเขียนด้วยคำสั่ง GOTO ผลลัพธ์คือโค้ดสปาเก็ตตี้และวิธีแก้ปัญหาคือการเขียนโปรแกรมแบบมีโครงสร้าง

ตัวชี้อาจมีลักษณะควบคุมได้ยากในโปรแกรมของเรา C ++ เริ่มต้นด้วยตัวชี้มากมาย แต่แนะนำให้ใช้การอ้างอิง ไลบรารี่อย่าง STL สามารถลดการพึ่งพาของเราได้บ้าง นอกจากนี้ยังมีสำนวนในการสร้างสมาร์ทพอยน์เตอร์ที่มีคุณสมบัติดีกว่าและ C ++ บางรุ่นจะอนุญาตให้อ้างอิงและจัดการโค้ด

วิธีปฏิบัติในการเขียนโปรแกรมเช่นการสืบทอดและ polymorphism ใช้ตัวชี้จำนวนมากเบื้องหลัง ภาษาเช่น Java กำจัดพอยน์เตอร์และใช้การรวบรวมขยะเพื่อจัดการข้อมูลที่จัดสรรแบบไดนามิกแทนที่จะขึ้นอยู่กับโปรแกรมเมอร์เพื่อจับคู่คำสั่งใหม่และลบทั้งหมด

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

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

th_id = omp_get_thread_num();
#pragma omp critical
{
  cout << "Hello World from thread " << th_id << '\n';
}

ตัวอย่างนี้เป็นข้อความที่ตัดตอนมาจาก: http://en.wikipedia.org/wiki/OpenMP

อีกวิธีหนึ่งการป้องกันเธรดที่คล้ายกันจากการใช้เซมาฟอร์พร้อมกับฟังก์ชั่น wait () และ signal () อาจมีลักษณะเช่นนี้:

wait(sem);
th_id = get_thread_num();
cout << "Hello World from thread " << th_id << '\n';
signal(sem);

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

ระบบเหล่านี้เช่น OpenMP กำจัดปัญหาเกี่ยวกับเซมาฟอร์หรือไม่
พวกเขาย้ายปัญหาไปที่อื่นหรือไม่?
ฉันจะแปลงเซมาฟอร์ที่ฉันชื่นชอบโดยใช้อัลกอริทึมเพื่อไม่ใช้เซมาฟอเรสอีกต่อไปได้อย่างไร


คุณกำลังพูดเรื่องอะไรกันแน่? คุณเห็นอะไร
svick

4
ไม่ได้หมายความว่าจะหยาบคาย แต่คุณสามารถกำจัดสามย่อหน้าแรกได้ พวกเขาไม่ได้อดทนกับคำถามของคุณและพวกเขาไปถึงข้อสรุปและจะสร้างข้อโต้แย้งมากมาย
dbracey

1
เดี๋ยวก่อนแก้ไขใหญ่ ฉันเอาคำตอบจิ้มไป คำถามยังคงเดินผ่าน GOTO พอยน์เตอร์การสืบทอดและพหุสัณฐาน แต่ในคำตอบของฉันฉันได้แยกปัญหาเหล่านี้ออกจากกันและได้มุ่งเน้นไปที่คำถาม
Stuart Marks

คำตอบ:


15

มีเทคนิคการเขียนโปรแกรมพร้อมกันและการปฏิบัติที่ไม่ควรใช้อีกต่อไปหรือไม่ ผมว่าใช่

หนึ่งเทคนิคการเขียนโปรแกรมพร้อมกันต้นที่ดูเหมือนว่าหายากในปัจจุบันคือการเขียนโปรแกรมขับเคลื่อนขัดจังหวะ นี่คือการทำงานของ UNIX ในปี 1970 ดูสิงโตอรรถกถาบน UNIXหรือบาคการออกแบบระบบปฏิบัติการยูนิกซ์ สั้น ๆ เทคนิคนี้คือการหยุดอินเตอร์รัปต์ชั่วคราวขณะจัดการโครงสร้างข้อมูลจากนั้นกู้อินเทอร์รัปต์หลังจากนั้น BSD SPL (9) หน้าคนมีตัวอย่างของการเข้ารหัสรูปแบบนี้ โปรดทราบว่าการขัดจังหวะนั้นเป็นแบบฮาร์ดแวร์และรหัสเป็นการรวมความสัมพันธ์โดยนัยระหว่างชนิดของการขัดจังหวะของฮาร์ดแวร์และโครงสร้างข้อมูลที่เกี่ยวข้องกับฮาร์ดแวร์นั้น ตัวอย่างเช่นรหัสที่จัดการบัฟเฟอร์ของดิสก์ I / O จำเป็นต้องหยุดการอินเตอร์รัปต์จากฮาร์ดแวร์ดิสก์คอนโทรลเลอร์ขณะทำงานกับบัฟเฟอร์เหล่านั้น

รูปแบบของการเขียนโปรแกรมนี้ถูกใช้โดยระบบปฏิบัติการบนฮาร์ดแวร์ตัวประมวลผลเดียว มันยากกว่ามากสำหรับแอปพลิเคชันที่จะจัดการกับการขัดจังหวะ ระบบปฏิบัติการบางระบบมีการขัดจังหวะซอฟต์แวร์และฉันคิดว่าผู้คนพยายามสร้างระบบการทำเกลียวหรือ coroutine ด้านบน แต่ก็ไม่ได้แพร่หลายมาก (แน่นอนไม่ได้อยู่ในโลกของ UNIX) ฉันสงสัยว่าการเขียนโปรแกรมแบบอินเตอร์รัปต์ถูก จำกัด อยู่ที่ระบบฝังตัวเล็ก ๆ หรือระบบเรียลไทม์

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

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

มีการสร้างไลบรารีเพิ่มเติมเช่นในjava.util.concurrentแพ็คเกจของ Java ซึ่งรวมถึงโครงสร้างข้อมูลที่หลากหลายพร้อมกันสูงและการสร้างเธรดร่วม สิ่งเหล่านี้สามารถรวมกับเทคนิคเพิ่มเติมเช่นการ จำกัด เธรดและการเปลี่ยนไม่ได้อย่างมีประสิทธิภาพ ดูJava Concurrency ในทางปฏิบัติโดย Goetz และ อัล สำหรับการสนทนาเพิ่มเติม น่าเสียดายที่โปรแกรมเมอร์หลายคนยังคงใช้โครงสร้างข้อมูลของตัวเองพร้อมกับล็อคและเงื่อนไขเมื่อพวกเขาควรจะใช้บางสิ่งบางอย่างเช่นConcurrentHashMapซึ่งผู้เขียนห้องสมุดได้ทำการยกของหนักขึ้นมาแล้ว

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

น่าเสียดายที่ยังไม่ชัดเจนว่าสามารถหลีกเลี่ยงได้ในทุกกรณีหรือไม่ การเขียนโปรแกรมจำนวนมากยังคงทำในลักษณะนี้ มันคงจะดีถ้าได้เห็นสิ่งนี้แทนที่ด้วยสิ่งอื่น คำตอบจากJarrod Robersonและdavidk01ชี้ไปที่เทคนิคต่าง ๆ เช่นข้อมูลที่ไม่เปลี่ยนรูปการเขียนโปรแกรมการทำงาน STM และการส่งข้อความ มีหลายสิ่งที่จะแนะนำพวกเขาและทุกคนกำลังพัฒนาอย่างแข็งขัน แต่ฉันไม่คิดว่าพวกเขาได้เปลี่ยนสถานะที่ไม่แน่นอนของการแบ่งปันแบบเก่าที่ดีมาแล้ว

แก้ไข: นี่คือคำตอบของฉันสำหรับคำถามเฉพาะตอนท้าย

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

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


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

+1 สำหรับ java.util.concurrent และเห็นด้วยกับความคิดเห็น - มันอยู่ใน JDK ตั้งแต่ 1.5 และฉันไม่ค่อยเห็นมันเคยใช้
MebAlone

1
ฉันขอให้คุณเน้นว่าการม้วนโครงสร้างของคุณเองมีความสำคัญเมื่อมีอยู่แล้ว มากมายแมลงมากมาย ...
corsiKa

ฉันไม่คิดว่ามันถูกต้องที่จะพูดว่า "เซมาฟอร์เป็นการล่วงหน้าผ่านอินเทอร์รัปต์เพราะเป็นซอฟต์แวร์ที่สร้างขึ้น (ไม่เกี่ยวข้องกับฮาร์ดแวร์) " เซมาฟอร์ขึ้นอยู่กับซีพียูเพื่อใช้คำสั่งเปรียบเทียบและสลับหรือเป็นตัวแปรหลายคอร์
Josh Pearce

@JoshPearce ของ semaphores แน่นอนจะดำเนินการโดยใช้โครงสร้างฮาร์ดแวร์ แต่พวกเขาเป็นนามธรรมที่เป็นอิสระจากการสร้างฮาร์ดแวร์ใด ๆ โดยเฉพาะอย่างยิ่งเช่น CAS ทดสอบและชุด cmpxchng ฯลฯ
สจ๊วร์ Marks

28

ตอบคำถาม

ฉันทามติทั่วไปจะถูกแบ่งปันร่วมกันในสถานะที่ไม่แน่นอนคือ Bad ™และรัฐที่ไม่เปลี่ยนรูปนั้นคือ Good ™ซึ่งได้รับการพิสูจน์แล้วว่ามีความถูกต้องและเป็นจริงอีกครั้งด้วยภาษาที่ใช้งานได้และภาษาที่จำเป็น

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

สถานที่ที่มีข้อบกพร่อง

คำถามนี้ขึ้นอยู่กับการเปรียบเทียบกับหลักฐานที่มีข้อบกพร่อง; นั่นGOTOเป็นปัญหาที่เกิดขึ้นจริงและถูกปฏิเสธในระดับสากลโดยนักออกแบบภาษาของ Intergalatic Universal และสหภาพวิศวกรรมซอฟต์แวร์ ©! หากไม่มีGOTOกลไก ASM จะไม่ทำงานเลย เช่นเดียวกับสถานที่ตั้งที่ตัวชี้แบบดิบเป็นปัญหากับ C หรือ C ++ และบางวิธีที่ตัวชี้สมาร์ทเป็นยาครอบจักรวาลพวกเขาไม่ได้

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

การศึกษาคือยาครอบจักรวาล

โปรแกรมเมอร์ของ Idiot คือdeprecatedทุกภาษาที่ได้รับความนิยมยังคงมีGOTOโครงสร้างไม่ว่าโดยตรงหรือโดยอ้อมและเป็นbest practiceเมื่อใช้อย่างถูกต้องในทุกภาษาที่มีโครงสร้างนี้

ตัวอย่าง: Java มีเลเบลและtry/catch/finallyทั้งคู่ทำงานเป็นGOTOคำสั่งโดยตรง

โปรแกรมเมอร์ Java ส่วนใหญ่ที่ฉันคุยด้วยไม่รู้ว่าimmutableจริง ๆ แล้วมีความหมายอะไรข้างนอกพวกเขาทำซ้ำthe String class is immutableกับซอมบี้เหมือนมองในสายตาของพวกเขา พวกเขาไม่รู้วิธีการใช้finalคำหลักอย่างถูกต้องเพื่อสร้างimmutableชั้นเรียน ดังนั้นฉันค่อนข้างมั่นใจว่าพวกเขาไม่รู้ว่าทำไมการส่งข้อความผ่านโดยใช้ข้อความที่ไม่เปลี่ยนรูปนั้นยอดเยี่ยมมาก


3
+1 คำตอบที่ดีเขียนอย่างชัดเจนและระบุรูปแบบพื้นฐานของสถานะที่ไม่แน่นอน IUBLDSEU ควรจะเป็นมส์ :)
Dibbeke

2
GOTO เป็น codeword สำหรับ 'ได้โปรดไม่ได้โปรดเริ่มสงครามเปลวไฟที่นี่ฉันหมาคู่คุณกล้า' คำถามนี้ทำให้ไฟลุกลาม แต่ไม่ได้คำตอบที่ดีเลย การกล่าวถึงอันทรงเกียรติของการเขียนโปรแกรมเชิงฟังก์ชั่นและการเปลี่ยนแปลงไม่ได้นั้นยอดเยี่ยม แต่ไม่มีเนื้อในข้อความเหล่านั้น
Evan Plaice

1
นี่ดูเหมือนจะเป็นคำตอบที่ขัดแย้งกัน ก่อนอื่นคุณพูดว่า "A is Bad, B is Good" จากนั้นคุณพูดว่า "Idiots is deprecated" สิ่งเดียวกันนี้ใช้ไม่ได้กับย่อหน้าแรกหรือไม่ ฉันไม่สามารถใช้คำตอบสุดท้ายของคุณและพูดว่า "สถานะที่ไม่แน่นอนที่แชร์ได้เป็นวิธีปฏิบัติที่ดีที่สุดเมื่อใช้อย่างถูกต้องในทุกภาษา" นอกจากนี้ "พิสูจน์" เป็นคำที่แข็งแกร่งมาก คุณไม่ควรจะใช้มันจนกว่าคุณจะมีจริงๆหลักฐาน
luiscubal

2
มันไม่ใช่ความตั้งใจของฉันที่จะเริ่มสงครามไฟ จนกระทั่ง Jarrod ตอบสนองต่อความคิดเห็นของฉันคิดว่า GOTO นั้นไม่ขัดแย้งและจะทำงานได้ดีในการเปรียบเทียบ เมื่อฉันเขียนคำถามมันไม่ได้เกิดขึ้นกับฉัน แต่ Dijkstra อยู่ที่พื้นดินเป็นศูนย์ทั้ง GOTOs และเซมาฟอร์ Edsger Dijkstra ดูเหมือนจะเป็นยักษ์สำหรับฉันและได้รับเครดิตด้วยการประดิษฐ์เซมาฟอร์ (1965) และต้น (2511) นักวิชาการทำงานเกี่ยวกับ GOTO วิธีการสนับสนุนของ Dijkstra นั้นมักจะดุร้ายและคาดคั้น การโต้เถียง / การเผชิญหน้าได้ผลกับเขา แต่ฉันแค่ต้องการความคิดเกี่ยวกับทางเลือกที่เป็นไปได้ในการสรุป
DeveloperDon

1
หลายโปรแกรมควรจะจำลองสิ่งต่าง ๆ ซึ่งในโลกแห่งความเป็นจริงไม่แน่นอน หากเวลา 5:37 น. วัตถุ # 451 ถือสถานะของบางสิ่งบางอย่างในโลกแห่งความเป็นจริงในขณะนั้น (5:37 น.) และสถานะของโลกแห่งความจริงก็เปลี่ยนแปลงไปในเวลาต่อมามันเป็นไปได้ที่เอกลักษณ์ของวัตถุที่เป็นตัวแทน สถานะของสิ่งต่าง ๆ ในโลกแห่งความเป็นจริงที่จะไม่เปลี่ยนรูป (นั่นคือสิ่งที่จะถูกแสดงโดยวัตถุ # 451) หรือวัตถุ # 451 จะไม่เปลี่ยนรูป แต่ไม่ทั้งสอง ในหลายกรณีการมีตัวตนไม่เปลี่ยนรูปจะมีประโยชน์มากกว่าการมีวัตถุ # 451 ไม่เปลี่ยนรูป
supercat

27

ความโกรธครั้งล่าสุดในแวดวงวิชาการดูเหมือนจะเป็นSoftware Transactional Memory (STM) และสัญญาว่าจะนำรายละเอียดทั้งหมดของการเขียนโปรแกรมแบบมัลติเธรดออกจากมือของโปรแกรมเมอร์โดยใช้เทคโนโลยีคอมไพเลอร์อัจฉริยะอย่างเพียงพอ เบื้องหลังมันยังคงล็อคและเซมาฟอร์ แต่คุณเป็นโปรแกรมเมอร์ไม่ต้องกังวลกับมัน ประโยชน์ของวิธีการนั้นยังไม่ชัดเจนและไม่มีคู่แข่งที่ชัดเจน

Erlangใช้การส่งข้อความและเอเจนต์สำหรับการทำงานพร้อมกันและเป็นรูปแบบที่ง่ายกว่าในการทำงานกับ STM ด้วยข้อความที่ส่งผ่านคุณไม่มีล็อคและเซมาฟอร์ที่ต้องกังวลเพราะแต่ละเอเจนต์ทำงานในจักรวาลขนาดเล็กของตัวเองดังนั้นจึงไม่มีข้อมูลเกี่ยวกับสภาพการแข่งขัน คุณยังมีคดีแปลก ๆ อยู่บ้าง แต่พวกมันก็อยู่ใกล้แค่ไหนก็มีความซับซ้อนเท่า livelocks และ deadlocks ภาษา JVM สามารถใช้ประโยชน์จากAkkaและได้รับประโยชน์ทั้งหมดจากการส่งข้อความและนักแสดง แต่ต่างจาก Erlang JVM ไม่ได้มีการสนับสนุนในตัวสำหรับนักแสดงดังนั้นในตอนท้ายของวัน Akka ยังคงใช้เธรดและล็อค แต่คุณในฐานะ โปรแกรมเมอร์ไม่ต้องกังวลกับมัน

รุ่นอื่นที่ฉันรู้ว่าไม่ได้ใช้ล็อคและเธรดคือการใช้ฟิวเจอร์สซึ่งเป็นรูปแบบการเขียนโปรแกรม async อีกรูปแบบหนึ่ง

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


+1 สำหรับคำใหม่ "รายละเอียดแบบไม่มีขน" มนุษย์ LOL ฉันแค่หยุดหัวเราะไม่ได้กับศัพท์ใหม่นี้ ฉันเดาว่าฉันจะใช้ "รหัสขน" ต่อจากนี้ไป
Saeed Neamati

1
@Saeed: ฉันเคยได้ยินการแสดงออกนั้นมาก่อนมันไม่ใช่เรื่องแปลก ฉันเห็นด้วยว่ามันตลกดี :-)
คาเมรอน

1
คำตอบที่ดี. .NET CLI ที่คาดคะเนยังมีการสนับสนุนสำหรับการส่งสัญญาณ (ต่างจากการล็อค) แต่ฉันยังไม่เจอตัวอย่างที่แทนที่การล็อกโดยสมบูรณ์ ฉันไม่แน่ใจว่า async มีค่าหรือไม่ หากคุณกำลังพูดถึงแพลตฟอร์มเช่น Javascript / NodeJs พวกเขาจะมีเธรดเดียวและดีกว่าเมื่อโหลด IO ระดับสูงเท่านั้นเพราะพวกเขามีความไวต่อการ จำกัด ทรัพยากรสูงสุดน้อยที่สุด (เช่นในบริบทของการโยนทิ้ง) สำหรับ CPU ที่มีปริมาณมากมีประโยชน์เพียงเล็กน้อย / ไม่มีประโยชน์ในการใช้การเขียนโปรแกรม async
Evan Plaice

1
คำตอบที่น่าสนใจฉันไม่เคยเจอฟิวเจอร์สมาก่อน นอกจากนี้ยังทราบว่าคุณยังสามารถมีการหยุดชะงักและlivelockในข้อความผ่านระบบเช่นErlang CSPช่วยให้คุณมีเหตุผลอย่างเป็นทางการเกี่ยวกับการหยุดชะงักและlivelockแต่ก็ไม่ได้ป้องกันด้วยตนเอง
Mark Booth

1
ฉันจะเพิ่มล็อคฟรีและรอโครงสร้างข้อมูลฟรีลงในรายการนั้น
หิน

3

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

นี้ใช้กับโครงสร้างการควบคุม: ifs, fors และแม้กระทั่งtry- catchบล็อกนี้เป็นเพียงนามธรรมมากกว่าgotos abstractions เหล่านี้มีประโยชน์เกือบทุกครั้งเพราะทำให้โค้ดของคุณอ่านง่ายขึ้น แต่มีหลายกรณีที่คุณจะต้องใช้goto(เช่นถ้าคุณเขียนชุดประกอบด้วยมือ)

สิ่งนี้ยังนำไปใช้กับการจัดการหน่วยความจำด้วย: C ++ smart pointers และ GC เป็นรูปแบบนามธรรมเหนือพอยน์เตอร์ดิบและการจัดสรร / จัดสรรหน่วยความจำด้วยตนเอง และบางครั้ง abstractions เหล่านี้ไม่เหมาะสมเช่นเมื่อคุณต้องการประสิทธิภาพสูงสุด

และเช่นเดียวกันกับมัลติเธรด: สิ่งต่าง ๆ เช่นฟิวเจอร์สและนักแสดงเป็นเพียงนามธรรมผ่านเธรดเซมาฟอร์ mutexes และคำสั่ง CAS abstractions ดังกล่าวสามารถช่วยให้คุณสร้างโค้ดของคุณให้อ่านง่ายขึ้นและยังช่วยให้คุณหลีกเลี่ยงข้อผิดพลาด แต่บางครั้งพวกเขาก็ไม่เหมาะสม

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


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

2

ใช่ แต่คุณไม่น่าจะเจอกับบางคน

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

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

ดังนั้นบางทีแนวปฏิบัติที่เลิกใช้แล้วก็คือการเขียนโค้ดที่เกิดขึ้นพร้อมกันด้วยตัวเองแทนที่จะใช้ไลบรารี่ที่ทันสมัยและผ่านการทดสอบมาแล้ว


ขอบคุณ ดูเหมือนว่ามีศักยภาพที่ยอดเยี่ยมสำหรับการใช้งานการเขียนโปรแกรมพร้อมกัน แต่อาจเป็นกล่องแพนโดร่าหากไม่ได้ใช้ในลักษณะที่มีระเบียบวินัย
DeveloperDon

2

Apple Grand Central Dispatch เป็นนามธรรมที่สง่างามที่เปลี่ยนความคิดของฉันเกี่ยวกับการทำงานพร้อมกัน การมุ่งเน้นไปที่คิวทำให้การใช้ตรรกะแบบอะซิงโครนัสเป็นลำดับความสำคัญง่ายกว่าในประสบการณ์ที่ต่ำต้อยของฉัน

เมื่อฉันโปรแกรมในสภาพแวดล้อมที่พร้อมใช้งานมันได้แทนที่การใช้เธรดล็อคและการสื่อสารระหว่างเธรดส่วนใหญ่


1

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

นี่เป็นเหตุผลหนึ่งว่าทำไมกรอบการทำงาน async หรือตามภารกิจ (เช่น Grand Central Dispatch หรือ TBB ของ Intel) เป็นที่นิยมมากขึ้นพวกเขาเรียกใช้งานรหัส 1 ในแต่ละครั้งทำให้เสร็จก่อนที่จะย้ายไปยังหน้าถัดไป แต่ละงานต้องใช้เวลาเล็กน้อยเว้นแต่คุณจะต้องการออกแบบเกลียว (เช่นงานแบบขนานของคุณนั้นอยู่ในคิวจริง ๆ ) งานที่ใช้ CPU มากจะถูกส่งผ่านไปยัง CPU หลักทางเลือกแทนที่จะประมวลผลในการประมวลผลเธรดเดียวงานทั้งหมด นอกจากนี้ยังง่ายต่อการจัดการหากไม่มีการประมวลผลแบบมัลติเธรดอย่างแท้จริงเกิดขึ้นเช่นกัน


เยี่ยมยอดขอบคุณที่อ้างอิงถึงเทคโนโลยีของ Apple และ Intel คำตอบของคุณชี้ให้เห็นความท้าทายในการจัดการเธรดไปที่ความเกี่ยวข้องหลักหรือไม่? ปัญหาประสิทธิภาพการทำงานแคชบางอย่างถูกปลดออกเนื่องจากตัวประมวลผลแบบมัลติคอร์อาจทำซ้ำแคช L1 ต่อหนึ่งคอร์? ตัวอย่างเช่น: software.intel.com/en-us/articles/… แคชความเร็วสูงสำหรับสี่คอร์ที่มีจำนวนแคชมากขึ้นสามารถเร็วกว่า 4x เร็วเท่ากับหนึ่งคอร์ที่มีแคชหายไปมากกว่าในข้อมูลเดียวกัน การคูณเมทริกซ์สามารถ การกำหนดเวลาแบบสุ่มของ 32 เธรดใน 4 คอร์ไม่สามารถทำได้ ลองใช้ความสัมพันธ์และรับ 32 คอร์
DeveloperDon

ไม่ใช่ว่ามันจะเป็นปัญหาเดียวกัน - ความเกี่ยวข้องหลักเพียงแค่อ้างถึงปัญหาที่งานถูกเด้งจากหลักหนึ่งไปยังอีกแกนหนึ่ง มันเป็นปัญหาเดียวกันหากงานถูกขัดจังหวะแทนที่ด้วยงานใหม่จากนั้นงานต้นฉบับจะดำเนินต่อไปบนแกนหลักเดียวกัน Intel กล่าวว่า: cache hits = fast, cache miss = slow โดยไม่คำนึงถึงจำนวนของคอร์ ฉันคิดว่าพวกเขากำลังพยายามเกลี้ยกล่อมให้คุณซื้อชิปของพวกเขามากกว่า AMD :)
gbjbaanb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.