มันเกี่ยวข้องกับคำถามนี้อย่างหลวม ๆ : std :: thread รวมอยู่ใน C ++ 11 หรือไม่? . แม้ว่าคำถามจะแตกต่างกัน แต่ความตั้งใจก็เหมือนกัน:
คำถามที่ 1: การใช้เธรดพูลของคุณเอง (หรือไลบรารีของบุคคลที่สาม) เพื่อหลีกเลี่ยงการสร้างเธรดที่มีราคาแพงหรือไม่
ข้อสรุปในคำถามอื่นคือคุณไม่สามารถพึ่งพาstd::thread
การรวมกลุ่มกันได้ (อาจเป็นหรือไม่ก็ได้) อย่างไรก็ตามstd::async(launch::async)
ดูเหมือนว่าจะมีโอกาสสูงกว่าที่จะถูกรวมกลุ่ม
ไม่คิดว่าจะถูกบังคับโดยมาตรฐาน แต่ IMHO ฉันคาดหวังว่าการใช้งาน C ++ 11 ที่ดีทั้งหมดจะใช้การรวมเธรดหากการสร้างเธรดช้า เฉพาะบนแพลตฟอร์มที่มีราคาไม่แพงในการสร้างเธรดใหม่ฉันคาดหวังว่าพวกเขาจะสร้างเธรดใหม่เสมอ
คำถาม 2: นี่เป็นเพียงสิ่งที่ฉันคิด แต่ฉันไม่มีข้อเท็จจริงที่จะพิสูจน์ได้ ฉันอาจจะเข้าใจผิด เป็นการคาดเดาที่มีการศึกษาหรือไม่?
สุดท้ายนี้ฉันได้ให้โค้ดตัวอย่างที่แสดงให้เห็นก่อนว่าฉันคิดว่าการสร้างเธรดสามารถแสดงได้อย่างไรasync(launch::async)
:
ตัวอย่างที่ 1:
thread t([]{ f(); });
// ...
t.join();
กลายเป็น
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
ตัวอย่างที่ 2: ไฟและลืมเธรด
thread([]{ f(); }).detach();
กลายเป็น
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
คำถามที่ 3: คุณต้องการasync
เวอร์ชันมากกว่าthread
เวอร์ชันหรือไม่?
ส่วนที่เหลือไม่ได้เป็นส่วนหนึ่งของคำถามอีกต่อไป แต่เพื่อการชี้แจงเท่านั้น:
เหตุใดจึงต้องกำหนดค่าตอบแทนให้กับตัวแปรดัมมี่
น่าเสียดายที่กองกำลังมาตรฐาน C ++ 11 ปัจจุบันที่คุณจับค่าส่งคืนstd::async
ไม่เช่นนั้นตัวทำลายจะถูกดำเนินการซึ่งบล็อกจนกว่าการดำเนินการจะสิ้นสุดลง บางคนถือว่าเป็นข้อผิดพลาดในมาตรฐาน (เช่นโดย Herb Sutter)
ตัวอย่างนี้จากcppreference.comแสดงให้เห็นอย่างสวยงาม:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
คำชี้แจงอื่น ๆ :
ฉันรู้ว่าสระว่ายน้ำด้ายอาจจะมีการใช้งานถูกต้องตามกฎหมายอื่น ๆ แต่ในคำถามนี้ผมสนใจเฉพาะในด้านของการหลีกเลี่ยงค่าใช้จ่ายราคาแพงการสร้างหัวข้อ
ฉันคิดว่ายังมีสถานการณ์ที่เธรดพูลมีประโยชน์มากโดยเฉพาะอย่างยิ่งหากคุณต้องการการควบคุมทรัพยากรมากขึ้น ตัวอย่างเช่นเซิร์ฟเวอร์อาจตัดสินใจที่จะจัดการเฉพาะคำขอจำนวนคงที่พร้อมกันเพื่อรับประกันเวลาตอบสนองที่รวดเร็วและเพื่อเพิ่มความสามารถในการคาดเดาการใช้หน่วยความจำ เธรดพูลควรจะใช้ได้ที่นี่
ตัวแปรเธรดโลคัลอาจเป็นอาร์กิวเมนต์สำหรับเธรดพูลของคุณเอง แต่ฉันไม่แน่ใจว่าเกี่ยวข้องกันในทางปฏิบัติหรือไม่:
- การสร้างเธรดใหม่โดย
std::thread
เริ่มต้นโดยไม่มีตัวแปรเธรดโลคัลเริ่มต้น บางทีนี่อาจไม่ใช่สิ่งที่คุณต้องการ - ในเธรดที่เกิดโดย
async
ฉันค่อนข้างไม่ชัดเจนเพราะเธรดอาจถูกนำกลับมาใช้ใหม่ได้ จากความเข้าใจของฉันไม่รับประกันว่าตัวแปร thread-local จะถูกรีเซ็ต แต่ฉันอาจเข้าใจผิด - ในทางกลับกันการใช้เธรดพูล (ขนาดคงที่) ของคุณเองจะช่วยให้คุณควบคุมได้เต็มที่หากคุณต้องการจริงๆ
std::async()
. ฉันยังอยากรู้ว่าพวกเขาสนับสนุนตัวทำลาย thread_local ที่ไม่สำคัญในกลุ่มเธรดได้อย่างไร
launch::async
นั้นก็จะถือว่ามันเป็นเพียงอย่างเดียวlaunch::deferred
และไม่ดำเนินการแบบอะซิงโครนัส - ดังนั้น libstdc ++ เวอร์ชันนั้นจึง "เลือก" เพื่อใช้การรอการตัดบัญชีเสมอเว้นแต่จะถูกบังคับ
std::async
อาจเป็นสิ่งที่สวยงามสำหรับประสิทธิภาพ - อาจเป็นระบบการรันงานสั้น ๆ มาตรฐานซึ่งได้รับการสนับสนุนโดยเธรดพูล ตอนนี้มันเป็นเพียงแค่std::thread
มีอึบางอย่างที่ติดอยู่เพื่อให้ฟังก์ชันเธรดสามารถคืนค่าได้ โอ้และพวกเขาได้เพิ่มฟังก์ชัน "รอการตัดบัญชี" ที่ซ้ำซ้อนซึ่งทับซ้อนกับงานโดยstd::function
สิ้นเชิง
std::async(launch::async)
ดูเหมือนว่าจะมีโอกาสสูงกว่าที่จะถูกรวมกลุ่ม" ไม่ฉันเชื่อว่ามันstd::async(launch::async | launch::deferred)
อาจจะถูกรวมเข้าด้วยกัน ด้วยlaunch::async
งานที่ควรจะเปิดใช้งานบนเธรดใหม่โดยไม่คำนึงถึงงานอื่น ๆ ที่กำลังทำงานอยู่ เมื่อใช้นโยบายlaunch::async | launch::deferred
แล้วการดำเนินการจะต้องเลือกนโยบาย แต่ที่สำคัญกว่านั้นคือต้องชะลอการเลือกนโยบาย นั่นคือสามารถรอจนกว่าเธรดในเธรดพูลจะพร้อมใช้งานจากนั้นจึงเลือกนโยบาย async