ฉันจะยุติเธรดใน C ++ 11 ได้อย่างไร


140

ฉันไม่จำเป็นต้องยุติเธรดอย่างถูกต้องหรือทำให้มันตอบสนองต่อคำสั่ง "ยุติ" ฉันสนใจที่จะยุติเธรดโดยใช้ C ++ 11 ล้วนๆ


7
นี่เป็นคำถามที่ดีสำหรับหัวข้อนี้: stackoverflow.com/questions/2790346/c0x-thread-interruption "ข้อกำหนดภาษาทั้งหมดระบุว่าการสนับสนุนไม่ได้รวมอยู่ในภาษา"
Nemanja Boric

คำตอบ:


139
  1. คุณสามารถโทรstd::terminate()จากเธรดใดก็ได้และเธรดที่คุณอ้างถึงจะสิ้นสุดลงอย่างมีผล

  2. คุณสามารถจัดเตรียม~thread()ให้ดำเนินการกับอ็อบเจ็กต์ของเธรดเป้าหมายโดยไม่มีการแทรกแซงjoin()หรือdetach()บนอ็อบเจ็กต์นั้น สิ่งนี้จะมีผลเช่นเดียวกับตัวเลือกที่ 1

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

ตัวเลือก 1 และ 2 จะไม่รั่วไหลทรัพยากรภายในกระบวนการ แต่จะยุติทุกเธรด

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

ไม่มีวิธีแบบพกพาใน C ++ 11 (ที่ฉันทราบ) ในการฆ่าเธรดเดี่ยวแบบไม่ร่วมมือกันในโปรแกรมมัลติเธรด (กล่าวคือโดยไม่ต้องฆ่าเธรดทั้งหมด) ไม่มีแรงจูงใจในการออกแบบคุณลักษณะดังกล่าว

A std::threadอาจมีฟังก์ชันสมาชิกนี้:

native_handle_type native_handle();

คุณอาจสามารถใช้สิ่งนี้เพื่อเรียกใช้ฟังก์ชันที่ขึ้นกับระบบปฏิบัติการเพื่อทำสิ่งที่คุณต้องการ ตัวอย่างเช่นในระบบปฏิบัติการของ Apple มีฟังก์ชันนี้อยู่และnative_handle_typeเป็นไฟล์pthread_t. หากคุณประสบความสำเร็จคุณมีแนวโน้มที่จะทำให้ทรัพยากรรั่วไหล


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

6
นอกจากนี้คุณยังสามารถโทรexit()หรือเอabort()ฟเฟกต์โดยรวมเดียวกัน
. 'สรรพนาม' ม.

2
# 1 เป็นเรื่องตลกและ @ChrisDodd พูดถูก เรื่องตลกอธิบายไว้ในคำตอบในประโยคแรกภายใต้ # 3 ดูคำตอบของ Nanno Langstraat และความคิดเห็นด้านล่าง
Howard Hinnant

44

คำตอบของ @Howard Hinnant ทั้งถูกต้องและครอบคลุม แต่อาจเข้าใจผิดหากอ่านเร็วเกินไปเนื่องจากstd::terminate()(กระบวนการทั้งหมด) มีชื่อเดียวกับ "การยุติ" ที่ @AlexanderVX มีอยู่ในใจ (1 เธรด)

สรุป: "ยุติ 1 เธรด + อย่างมีประสิทธิภาพ (เธรดเป้าหมายไม่ให้ความร่วมมือ) + C ++ บริสุทธิ์ 11 = ไม่มีทาง"


16
อ่า # 1 ของฉันตลกจริงๆ คุณไม่จำเป็นต้องระบุเธรดที่คุณต้องการบังคับให้สิ้นสุด ระบบเพียงแค่รู้อย่างน่าอัศจรรย์ว่าคุณต้องการสิ้นสุดอย่างใดและลงมือทำ!
Howard Hinnant

11
ใช่std::terminate()คำตอบก็เหมือนกับเรื่องราวของจินน์ที่ซุกซนแบบคลาสสิก มันตอบสนองทุกสิ่งในความปรารถนาของ OP ที่มีต่อจดหมายแม้ว่าอาจจะไม่ได้เป็นไปในทางที่เขาหมายถึงก็ตาม อารมณ์ขันที่พูดน้อยทำให้ฉันยิ้ม :-)
Nanno Langstraat

3
เพียงแค่ต้องป้องกันไม่ให้มือใหม่ C ++ ที่ไร้เดียงสาตั้งความหวังไว้ไกลเกินไป / นานเกินไป
Nanno Langstraat

2
ระบุไว้อย่างหรูหรา :-)
Howard Hinnant

@NannoLangstraat dammnit ... ฉันมีความหวังสูงมากจนกระทั่งฉันอ่านต่อ: (.. ฮ่า ๆ ๆ ๆ ๆ +1 ทุกรอบ
:)

13

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

ลองเปรียบเทียบแนวคิดหลายกระบวนการและมัลติเธรดเพื่อทำความเข้าใจข้อดีและข้อ จำกัด ของแนวคิดที่สองให้ดีขึ้น

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

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

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


8
นี่ไม่ใช่คำตอบที่มีประโยชน์มากนัก การประมวลผลหลายรายการไม่เกี่ยวข้องที่นี่และข้อสังเกตเกี่ยวกับมัลติเธรดค่อนข้างกว้าง
MSalters

5
สิ่งที่ฉันอยากจะพูดและพูดในรายละเอียดก็คือโมเดลมัลติเธรดไม่ได้ให้วิธีการยุติเธรดที่มีประสิทธิภาพอย่างเป็นทางการ C ++ ต้องการทำตามแบบจำลองที่ชัดเจนรวมถึงโมเดลหน่วยความจำโมเดลมัลติเธรดเป็นต้นวิธีการยกเลิกเธรดที่มีประสิทธิภาพโดยเนื้อแท้ไม่ปลอดภัย หากคณะกรรมการมาตรฐาน C ++ ถูกบังคับให้เพิ่มลงใน C ++ จะมีคำสั่งถัดไป "Method terminate () ยกเลิกการทำงานของเธรด Behavior undefined" ซึ่งจะเป็นเหมือน "make some magic and (อาจ) ยกเลิกเธรด ".
ZarathustrA

C # มีคุณสมบัตินี้
Derf Skren

@Derf Skren "ในความเป็นจริงไม่มีภาษาใด ๆ หรือระบบปฏิบัติการใด ๆ ที่ให้สิ่งอำนวยความสะดวกแก่คุณสำหรับการยุติเธรดแบบอะซิงโครนัสแบบกะทันหันโดยไม่มีคำเตือนว่าห้ามใช้" (c) ZarathustrA "หมายเหตุ: สำคัญ! ควรใช้วิธี Thread.Abort ด้วยความระมัดระวัง โดยเฉพาะอย่างยิ่งเมื่อคุณเรียกมันเพื่อยกเลิกเธรดอื่นที่ไม่ใช่เธรดปัจจุบันคุณไม่ทราบว่าโค้ดใดทำงานหรือไม่สามารถดำเนินการได้ ... "(c) ลิงค์ Microsoft: docs.microsoft.com/en-us/dotnet/ api / …
ZarathustrA

12

เคล็ดลับในการใช้ฟังก์ชันที่ขึ้นกับระบบปฏิบัติการเพื่อยุติเธรด C ++:

  1. std::thread::native_handle()สามารถได้รับถูกต้องชนิดจับพื้นเมืองของเธรดก่อนที่จะเรียกหรือjoin() detach()หลังจากนั้นnative_handle()ส่งกลับ 0 - pthread_cancel()จะ coredump

  2. เพื่อประสิทธิภาพในการเรียกใช้ฟังก์ชันการเลิกจ้างด้ายพื้นเมือง (เช่นpthread_cancel()), คุณจำเป็นต้องบันทึกจับพื้นเมืองก่อนที่จะเรียกหรือstd::thread::join() std::thread::detach()เพื่อให้เทอร์มิเนเตอร์เนทีฟของคุณมีแฮนเดิลเนทีฟที่ใช้ได้เสมอ

คำอธิบายเพิ่มเติมโปรดดูที่: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread


9

ฉันเดาว่าเธรดที่ต้องฆ่าไม่ว่าจะอยู่ในโหมดรอแบบใดก็ตามหรือทำงานหนัก ฉันขอแนะนำให้ใช้วิธีที่ "ไร้เดียงสา"

กำหนดบูลีนส่วนกลางบางส่วน:

std::atomic_bool stop_thread_1 = false;

ใส่รหัสต่อไปนี้ (หรือที่คล้ายกัน) ในประเด็นสำคัญหลาย ๆ จุดในลักษณะที่จะทำให้ฟังก์ชันทั้งหมดใน call stack กลับมาจนกว่าเธรดจะสิ้นสุดลงตามธรรมชาติ:

if (stop_thread_1)
    return;

จากนั้นเพื่อหยุดเธรดจากเธรด (หลัก) อื่น:

stop_thread_1 = true;
thread1.join ();
stop_thread_1 = false; //(for next time. this can be when starting the thread instead)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.