c ++ เธรดภายในเพื่อวนซ้ำค่าที่ไม่ถูกต้อง


19

ฉันพยายามที่จะเข้าใจ Multi-Threading ใน c ++ แต่ฉันติดอยู่กับปัญหานี้: ถ้าฉันเริ่มใช้เธรดในลูปพวกเขาพิมพ์ค่าผิด นี่คือรหัส:

#include <iostream>
#include <list>
#include <thread>

void print_id(int id){
    printf("Hello from thread %d\n", id);
}

int main() {
    int n=5;
    std::list<std::thread> threads={};
    for(int i=0; i<n; i++ ){
        threads.emplace_back(std::thread([&](){ print_id(i); }));
    }
    for(auto& t: threads){
        t.join();
    }
    return 0;
}

ฉันคาดหวังว่าจะได้รับการพิมพ์ค่า 0,1,2,3,4 แต่ฉันมักจะได้รับค่าเดียวกันสองครั้ง นี่คือผลลัพธ์:

Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5

สิ่งที่ฉันหายไป?


7
ผ่านโดยมูลค่าให้กับแลมบ์ดาi [i]
rafix07

1
มันน่าสังเกตว่าการใช้งานของemplace_backแปลก: ใช้เวลารายการของอาร์กิวเมนต์และส่งผ่านไปยังที่คอนสตรัคสำหรับemplace_back std::threadคุณผ่านอินสแตนซ์ (rvalue) ของstd::threadดังนั้นจะสร้างเธรดแล้วย้ายเธรดนั้นไปยังเวกเตอร์ push_backการดำเนินงานที่มีการแสดงที่ดีขึ้นโดยวิธีการที่พบมากขึ้น มันจะเหมาะสมกว่าที่จะเขียนthreads.emplace_back([i](){ print_id(i); });(สร้างในสถานที่) หรือthreads.push_back(std::thread([i](){ print_id(i); }));(สร้าง + ย้าย) ซึ่งค่อนข้างเป็นสำนวนมากกว่า
Milo Brandt

คำตอบ:


17

[&]ไวยากรณ์ที่เป็นสาเหตุของiการถูกจับโดยการอ้างอิง ดังนั้นค่อนข้างบ่อยดังนั้นiจะก้าวหน้าต่อไปเมื่อเธรดรันมากกว่าที่คุณคาดไว้ ยิ่งพฤติกรรมของรหัสของคุณไม่ได้กำหนดถ้าไม่อยู่iในขอบเขตก่อนที่เธรดจะทำงาน

การจับภาพiตามค่า - std::thread([i](){ print_id(i); })คือการแก้ไข


2
หรือใช้น้อยกว่าและไม่แนะนำให้ใช้บ่อยstd::thread([=](){ print_id(i); })
Wander3r

3
พฤติกรรมนี้ยังไม่ได้กำหนดเนื่องจากนี่เป็นการแย่งข้อมูลบน (ไม่ใช่อะตอมมิก) iด้วยการเขียนเธรดหลักและเธรดอื่น ๆ ที่อ่าน
วอลนัท

6

ปัญหาสองประการ:

  1. คุณไม่สามารถควบคุมได้เมื่อเธรดรันซึ่งหมายความว่าค่าของตัวแปรiในแลมบ์ดาอาจไม่ใช่สิ่งที่คุณคาดหวัง

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

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


5

สิ่งหนึ่ง:
ไม่ต้องรอจนกว่าจะมีเสมอลำดับ: 0, 1, 2, 3, ... เพราะโหมดการดำเนินการ multithreading มีความจำเพาะ A: indeterminism

Indeterminism หมายถึงการดำเนินการของโปรแกรมเดียวกันภายใต้เงื่อนไขเดียวกันให้ผลลัพธ์ที่แตกต่าง

นี่เป็นเพราะความจริงที่ว่าระบบปฏิบัติการจัดตารางเวลาเธรดที่แตกต่างจากการดำเนินการหนึ่งไปยังอีกขึ้นอยู่กับพารามิเตอร์หลายอย่าง: โหลด CPU, ลำดับความสำคัญของกระบวนการอื่น ๆ , การหยุดชะงักของระบบที่เป็นไปได้ ...

ตัวอย่างของคุณมีเพียง 5 เธรดดังนั้นจึงง่ายลองเพิ่มจำนวนเธรดและตัวอย่างเช่นใส่ sleep ในฟังก์ชันการประมวลผลคุณจะเห็นว่าผลลัพธ์อาจแตกต่างจากการดำเนินการหนึ่งไปอีกรายการหนึ่ง

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