มันฉลาดที่จะแทนที่บูสต์ :: thread และ boost :: mutex ด้วย c ++ 11 เทียบเท่าหรือไม่?


153

แรงจูงใจ: เหตุผลที่ฉันคิดว่าผู้จัดการโครงการอัจฉริยะของฉันคิดว่าการเพิ่มเป็นอีกการพึ่งพาและมันน่ากลัวเพราะ "คุณขึ้นอยู่กับมัน" (ฉันพยายามอธิบายคุณภาพของการส่งเสริมแล้วเลิกหลังจากนั้นสักครู่ :( ) เหตุผลที่เล็กกว่าที่ฉันต้องการทำคือฉันต้องการเรียนรู้คุณสมบัติ c ++ 11 เพราะผู้คนจะเริ่มเขียนโค้ดลงไปดังนั้น:

  1. มีการแมป 1: 1 ระหว่าง#include<thread> #include<mutex>และเพิ่มการเทียบเท่าหรือไม่
  2. คุณจะพิจารณาความคิดที่ดีที่จะเปลี่ยนสิ่งที่เพิ่มด้วย C ++ 11
    สิ่ง การใช้งานของฉันเป็นพื้นฐาน แต่มีตัวอย่างเมื่อ std ไม่เสนอเพิ่มอะไรบ้าง หรือ (ดูหมิ่น) ในทางกลับกัน?

PS ฉันใช้ GCC ดังนั้นส่วนหัวจึงอยู่ที่นั่น


46
แนวทางการเข้ารหัส IMO ของ Google นั้นโง่ในหลาย ๆ ด้าน ... เช่น พวกเขาไม่อนุญาตให้ใช้อัตโนมัติจาก C ++ 11 ... :)
NoSenseEtAl

5
หลักเกณฑ์การอ้างอิง: [อัตโนมัติ] ความสามารถในการอ่าน hampers [เพราะลบออก] ความซ้ำซ้อนที่ตรวจสอบแล้ว (เช่นชื่อประเภท) ที่อาจเป็นประโยชน์ต่อผู้อ่าน
Andrew Tomazos

31
สำหรับ (auto it = v.begin () ... :)
NoSenseEtAl

15
@ AndrewTomazos-Fathomling: จริงเหรอ? โดยส่วนตัวฉันไม่คิดว่าฉันเคยใส่ใจเกี่ยวกับตัววนซ้ำจริง ๆ (อาจจะสองสามครั้ง) เพียงแค่ปฏิบัติการที่สนับสนุน ...
Grizzly

16
btw google แก้ไขแนวทางโง่ดังนั้นในที่สุดพวกเขาก็อนุญาตอัตโนมัติ
NoSenseEtAl

คำตอบ:


192

มีความแตกต่างหลายอย่างระหว่าง Boost.Thread และไลบรารีเธรดมาตรฐาน C ++ 11:

  • Boost รองรับการยกเลิกเธรด C ++ 11 เธรดไม่ได้
  • C ++ 11 รองรับstd::asyncแต่ Boost ไม่รองรับ
  • Boost มีการboost::shared_mutexล็อคหลายผู้อ่าน / ผู้เขียนเดี่ยว คล้ายstd::shared_timed_mutexจะใช้ได้เฉพาะตั้งแต่ C ++ 14 ( N3891 ) ในขณะที่std::shared_mutexจะใช้ได้เฉพาะตั้งแต่ C ++ 17 ( N4508 )
  • การหมดเวลาของ C ++ 11 นั้นแตกต่างจากการหมดเวลาของ Boost (แม้ว่าจะมีการเปลี่ยนแปลงในไม่ช้าในขณะนี้ Boost.Chrono ได้รับการยอมรับ)
  • ชื่อบางชื่อแตกต่างกัน (เช่นboost::unique_futurevs std::future)
  • ซีแมนทิกส์การส่งอากิวเมนต์ของstd::threadมีความแตกต่างกับboost::thread--- การใช้ Boost boost::bindซึ่งต้องการอาร์กิวเมนต์ที่คัดลอกได้ std::threadอนุญาตให้ใช้ชนิดย้ายอย่างเดียวเช่นstd::unique_ptrส่งผ่านเป็นอาร์กิวเมนต์ เนื่องจากการใช้งานboost::bindความหมายของตัวยึดตำแหน่งเช่น_1ในนิพจน์ผูกแบบซ้อนอาจแตกต่างกันเช่นกัน
  • หากคุณไม่ได้โทรออกอย่างชัดเจนjoin()หรือdetach()ผู้ดำเนินการboost::threaddestructor และการมอบหมายจะโทรหาdetach()วัตถุเธรดที่ถูกทำลาย / กำหนดให้ ด้วยstd::threadวัตถุC ++ 11 สิ่งนี้จะส่งผลให้มีการเรียกstd::terminate()และยกเลิกแอปพลิเคชัน

เมื่อต้องการชี้แจงจุดเกี่ยวกับพารามิเตอร์การย้ายอย่างเดียวต่อไปนี้คือ C ++ 11 ที่ถูกต้องและถ่ายโอนความเป็นเจ้าของintจากชั่วคราวstd::unique_ptrไปยังพารามิเตอร์ของf1เมื่อเธรดใหม่เริ่มต้นขึ้น อย่างไรก็ตามหากคุณใช้งานboost::threadจะไม่สามารถใช้งานได้เนื่องจากใช้เป็นการboost::bindภายในและstd::unique_ptrไม่สามารถคัดลอกได้ นอกจากนี้ยังมีข้อผิดพลาดในไลบรารีเธรด C ++ 11 ที่มาพร้อมกับ GCC ที่ป้องกันการทำงานนี้เนื่องจากจะใช้std::bindในการปรับใช้ที่นั่นด้วย

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

หากคุณใช้ Boost คุณอาจเปลี่ยนไปใช้เธรด C ++ 11 ได้ค่อนข้างลำบากหากคอมไพเลอร์ของคุณรองรับ (เช่น GCC เวอร์ชันล่าสุดบน Linux มีการใช้งาน-std=c++0xไลบรารี่เธรด C ++ 11 ในโหมด)

หากคอมไพเลอร์ของคุณไม่รองรับเธรด C ++ 11 คุณอาจสามารถใช้งานบุคคลที่สามเช่นJust :: Threadแต่สิ่งนี้ยังคงเป็นการพึ่งพา


1
มีวิธีการล็อก / ปลดล็อกแยกต่างหากสำหรับผู้อ่านและนักเขียน ( lock/ unlockสำหรับนักเขียนเทียบกับ 'lock_shared / unlock_shared' สำหรับผู้อ่าน) ผู้อ่านหลายคนสามารถโทร lock_shared โดยไม่ปิดกั้นตราบใดที่ไม่มีผู้เขียนใช้งาน
Dave S

2
shared_mutexเอกสารอยู่ที่boost.org/doc/libs/1_47_0/doc/html/thread/... คุณล็อก mutex เป็นแบบแบ่งใช้หรือแบบเอกสิทธิ์เฉพาะบุคคลจากนั้นใช้ฟังก์ชันปลดล็อกที่เกี่ยวข้อง คุณยังสามารถใช้ประเภท RAII เพื่อทำสิ่งนี้ ( shared_lockใช้การล็อกการอ่านที่ใช้ร่วมกันและlock_guardและunique_lockการล็อคแบบเอกสิทธิ์) ฉันพยายามอธิบายประเด็นเกี่ยวกับประเภทการย้ายเท่านั้น
Anthony Williams

3
อีกหนึ่งสิ่งเล็ก ๆ น้อย ๆ ที่ทำให้ฉันสะดุด: ในการเพิ่ม, destructor ของเธรดที่กำลังรันจะแยกออก ( boost.org/doc/libs/1_47_0/doc/html/thread/ … ), ในขณะที่ C ++, destructor ของการเรียกเธรดที่กำลังรันจะสิ้นสุดลง () (FDIS 30.3.1.3)
Cubbi

3
ใน C ++ 11 ฟังก์ชันการทำงานที่ถูกปกคลุมด้วยtry_scoped_lock std::unique_lockมีคอนสตรัคที่ใช้ mutex และเป็นstd::try_to_lockและจากนั้นจะเรียกtry_lock()ใน mutex lock()มากกว่า ดูstdthread.co.uk/doc/headers/mutex/unique_lock/ …
Anthony Williams

4
ใช่ Boost.Thread ได้ใกล้เคียงกับมาตรฐาน C ++ 11 มากขึ้นตั้งแต่ฉันเขียนสิ่งนี้เป็นหลักเนื่องจากการทำงานของ Vicente Botet
Anthony Williams

24

std::threadเป็นแบบจำลองส่วนใหญ่หลังจากboost::threadมีความแตกต่าง :

  • บูสต์ที่ไม่สามารถคัดลอกได้ของ one-handle-maps-to-one-os-thread แต่เธรดนี้สามารถเคลื่อนย้ายเพื่ออนุญาตให้เธรดที่ส่งคืนจากฟังก์ชันโรงงานและวางลงในคอนเทนเนอร์
  • ข้อเสนอนี้เพิ่มการยกเลิกไปboost::threadซึ่งเป็นภาวะแทรกซ้อนที่สำคัญ การเปลี่ยนแปลงนี้มีผลกระทบอย่างมากไม่เพียง แต่ในเธรดเท่านั้น แต่ยังมีเธรด C ++ ที่เหลืออีกด้วย เป็นที่เชื่อกันว่าการเปลี่ยนแปลงครั้งใหญ่นี้มีความสมเหตุสมผลเนื่องจากประโยชน์
    • ตัวทำลายเธรดในขณะนี้จะต้องเรียกยกเลิกก่อนที่จะแยกเพื่อหลีกเลี่ยงการรั่วไหลของกระทู้เด็กโดยไม่ได้ตั้งใจเมื่อหัวข้อแม่จะถูกยกเลิก
    • ตอนนี้จำเป็นต้องมีสมาชิก detach เพื่อเปิดใช้งาน detaching โดยไม่ยกเลิก
  • แนวคิดของการจัดการเธรดและเอกลักษณ์ของเธรดได้ถูกแยกออกเป็นสองคลาส (ซึ่งเป็นคลาสเดียวกันในboost::thread) นี่คือการสนับสนุนการจัดการที่ง่ายขึ้นและการจัดเก็บข้อมูลประจำตัวของเธรด
  • ความสามารถในการสร้างเธรด id ซึ่งรับประกันได้ว่าจะเปรียบเทียบเท่ากับไม่มีเธรดที่สามารถเข้าร่วมได้อื่น ๆ ได้ถูกเพิ่ม ( boost::threadไม่มีสิ่งนี้) สิ่งนี้มีประโยชน์สำหรับโค้ดที่ต้องการทราบว่ามันจะถูกดำเนินการโดยเธรดเดียวกับการโทรก่อนหน้า (mutexes แบบเรียกซ้ำเป็นตัวอย่างที่เป็นรูปธรรม)
  • มี "ประตูหลัง" ที่จะได้รับการจัดการเธรดพื้นเมืองเพื่อให้ลูกค้าสามารถจัดการกระทู้โดยใช้ระบบปฏิบัติการพื้นฐานถ้าต้องการ

นี่คือจากปี 2007 ดังนั้นบางจุดไม่ถูกต้องอีกต่อไป: boost::threadมีnative_handleฟังก์ชั่นในขณะนี้และในฐานะที่เป็นผู้แสดงความเห็นชี้ให้เห็นว่าstd::threadไม่มีการยกเลิกอีกต่อไป

ฉันไม่สามารถพบความแตกต่างอย่างมีนัยสำคัญใด ๆ ระหว่างและboost::mutexstd::mutex


2
std::threadไม่มีการยกเลิก มันเป็นอย่างboost::threadนั้น!
Anthony Williams

@ อันธพาลคุณแน่ใจหรือว่าคุณไม่ได้ตั้งใจinterrupt()เพิ่ม :: ด้าย? นอกจากนี้ยังปรากฏว่าเป็นข้อเสนอดั้งเดิมซึ่งเปลี่ยนไปตั้งแต่ปี 2007
อเล็กซ์ B

4
ใช่การยกเลิกการส่งเสริมถูกเรียกว่า "การขัดจังหวะ" ใช่นี่เป็นข้อเสนอเก่า ๆ แบบร่างสาธารณะล่าสุดของมาตรฐาน C ++ 11 (ซึ่งรวมไลบรารีเธรด) คือopen-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams

6

std::threadมีเหตุผลอย่างใดอย่างหนึ่งไม่ได้ที่จะโยกย้ายไปเป็น

หากคุณใช้การเชื่อมโยงแบบคงที่std::threadไม่สามารถใช้งานได้เนื่องจากข้อบกพร่อง gcc / คุณสมบัติเหล่านี้:

กล่าวคือถ้าคุณโทรstd::thread::detachหรือstd::thread::joinมันจะนำไปสู่การยกเว้นหรือผิดพลาดในขณะที่boost::threadทำงานได้ดีในกรณีเหล่านี้


ผมเห็นว่าหนึ่งในข้อผิดพลาดคือการยืนยันและอื่น ๆ libpthread.aที่ไม่ถูกต้องด้วยความคิดเห็นว่านักข่าวควรจะมีการเชื่อมโยงกับ คุณแน่ใจในสิ่งที่คุณพูดอย่างแน่นอนหรือไม่
einpoklum

1
@einpoklum คุณควรจะสามารถที่จะทำให้การทำงานโดยใช้Wl,--whole-archive -lpthread -Wl,--no-whole-archiveดูคำตอบนี้เช่นstackoverflow.com/a/23504509/72178 แต่มันไม่ได้เป็นวิธีที่ตรงไปตรงมามากที่จะเชื่อมโยงกับlibpthread.aและยังถือว่าเป็นความคิดที่ไม่ดี
ks1322

4
เราสามารถสมมติว่าข้อบกพร่องเหล่านี้ได้รับการแก้ไขได้หรือไม่เนื่องจากตอนนี้เป็นปี 2559 ข้อผิดพลาดถูกโพสต์ในปี 2012 และจาก gcc 4.9.2 สนับสนุนอย่างเป็นทางการ C ++ 11 ดังนั้นเราจึงไม่สามารถบ่น C ++ 11 ก่อนการสนับสนุนอย่างเป็นทางการ
สแปลช

6

กรณีองค์กร

หากคุณกำลังเขียนซอฟต์แวร์สำหรับองค์กรที่ต้องการใช้งานระบบปฏิบัติการในระดับปานกลางถึงขนาดใหญ่และสร้างด้วยคอมไพเลอร์และคอมไพเลอร์เวอร์ชันต่าง ๆ (โดยเฉพาะซอฟต์แวร์ที่ค่อนข้างเก่าแก่) ในระบบปฏิบัติการเหล่านั้น C ++ 11 พร้อมกันในตอนนี้ นั่นหมายความว่าคุณไม่สามารถใช้และฉันจะแนะนำให้ใช้std::threadboost::thread

กรณีเริ่มต้นพื้นฐาน / เทค

หากคุณกำลังเขียนสำหรับระบบปฏิบัติการหนึ่งหรือสองระบบคุณรู้แน่ว่าคุณจะต้องสร้างด้วยคอมไพเลอร์สมัยใหม่ที่รองรับ C ++ 11 เป็นส่วนใหญ่ (เช่น VS2015, GCC 5.3, Xcode 7) และคุณยังไม่ได้สร้าง ขึ้นอยู่กับห้องสมุดเพิ่มแล้วก็std::threadอาจเป็นตัวเลือกที่ดี

ประสบการณ์ของฉัน

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


1
@UmNyobe เขาพูดถูก การใช้เธรด C ++ 11 จำนวนมากเสียแล้วฉันรู้สึกประหลาดใจที่ผู้คนพิจารณาใช้
StaceyGirl


1

เกี่ยวกับ std :: shared_mutex ที่เพิ่มใน C ++ 17

คำตอบอื่น ๆ ที่นี่ให้ภาพรวมที่ดีมากของความแตกต่างโดยทั่วไป อย่างไรก็ตามมีปัญหาหลายอย่างเกี่ยวกับการstd::shared_mutexแก้ไขการเพิ่มนั้น

  1. มิวเตชั่นที่เลื่อนได้ std::threadเหล่านี้จะหายไปจาก พวกเขาช่วยให้ผู้อ่านจะได้รับการอัปเกรดเป็นนักเขียนโดยไม่อนุญาตให้นักเขียนคนอื่น ๆ จะได้รับในก่อนที่คุณจะ สิ่งเหล่านี้ช่วยให้คุณสามารถทำสิ่งต่าง ๆ เช่นการคำนวณขนาดใหญ่ล่วงหน้า (เช่นการทำดัชนีโครงสร้างข้อมูลใหม่) เมื่ออยู่ในโหมดอ่านจากนั้นอัปเกรดเป็นการเขียนเพื่อใช้งานการทำดัชนีในขณะที่ถือล็อกการเขียนเพียงระยะเวลาสั้น ๆ

  2. ความเป็นธรรม หากคุณมีกิจกรรมการอ่านอย่างต่อเนื่องกับ a std::shared_mutexนักเขียนของคุณจะถูก softlocked ไปเรื่อย ๆ นี่เป็นเพราะหากผู้อ่านรายอื่นเข้ามาพวกเขาจะได้รับความสำคัญเสมอ ด้วยในที่สุดboost:shared_mutexกระทู้ทั้งหมดจะได้รับความสำคัญ (1)ทั้งผู้อ่านและนักเขียนจะไม่ได้รับอาหาร

tl; dr ของสิ่งนี้คือถ้าคุณมีระบบที่มีปริมาณงานสูงมากโดยไม่มีการหยุดทำงานและการช่วงชิงที่สูงมากstd::shared_mutexจะไม่ทำงานให้คุณโดยไม่ต้องสร้างระบบที่มีลำดับความสำคัญสูงด้วยตนเอง boost::shared_mutexจะทำงานนอกกรอบแม้ว่าคุณอาจจำเป็นต้องใช้คนจรจัดในบางกรณี ฉันขอยืนยันว่าstd::shared_mutexพฤติกรรมของมันคือบั๊กแฝงที่รอให้เกิดขึ้นในโค้ดส่วนใหญ่ที่ใช้งาน

(1) อัลกอริทึมที่เกิดขึ้นจริงจะใช้จะขึ้นอยู่กับตารางเวลาด้ายระบบปฏิบัติการ จากประสบการณ์ของฉันเมื่อการอ่านมีความอิ่มตัวมีการหยุดทำงานนานขึ้น (เมื่อได้รับการล็อกการเขียน) บน Windows มากกว่าบน OSX / Linux


0

ฉันพยายามใช้ shared_ptr จาก std แทน boost และฉันพบข้อผิดพลาดในการใช้ gcc ของคลาสนี้ แอปพลิเคชันของฉันหยุดทำงานเนื่องจาก destructor เรียกว่าสองครั้ง (คลาสนี้ควรปลอดภัยต่อเธรดและไม่ควรสร้างปัญหาดังกล่าว) หลังจากย้ายเพื่อเพิ่ม :: shared_ptr ปัญหาทั้งหมดหายไป การใช้งานปัจจุบันของ C ++ 11 ยังไม่ครบกำหนด

Boost ยังมีคุณสมบัติอื่น ๆ อีกมากมาย ตัวอย่างเช่นส่วนหัวในรุ่น std ไม่มี serializer ให้กับสตรีม (เช่น cout << ระยะเวลา) Boost มีหลายไลบรารีที่ใช้ของตนเองและอื่น ๆ ที่เทียบเท่า แต่ไม่ร่วมมือกับรุ่นมาตรฐาน

ในการสรุป - ถ้าคุณมีแอปพลิเคชันที่เขียนโดยใช้บูสต์มันจะปลอดภัยกว่าที่จะเก็บโค้ดไว้เพราะแทนที่จะพยายามย้ายไปใช้มาตรฐาน C ++ 11


4
shared_ptrdestructor ไม่จำเป็นต้องเป็นด้ายปลอดภัยก็ไม่ได้กำหนดพฤติกรรมที่จะมีหนึ่งหัวข้อการเข้าถึงวัตถุในขณะที่หัวข้ออื่นคือทำลายมัน หากคุณคิดว่าคุณพบข้อผิดพลาดใน shared_ptr ของ GCC โปรดรายงานข้อผิดพลาดมิฉะนั้นความสมดุลของความน่าจะเป็นที่คุณใช้ผิด
Jonathan Wakely
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.