c ++ Chủ đề bên trong cho vòng lặp in giá trị sai


19

Tôi đang cố gắng hiểu Đa luồng trong c ++, nhưng tôi bị mắc kẹt trong vấn đề này: nếu tôi khởi chạy các luồng trong một vòng lặp for thì chúng in sai các giá trị. Đây là mã:

#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;
}

Tôi đã mong đợi được in các giá trị 0,1,2,3,4 nhưng tôi thường nhận được cùng một giá trị hai lần. Đây là đầu ra:

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

Tôi đang thiếu gì?


7
Truyền itheo giá trị cho lambda [i],.
rafix07

1
Điều đáng chú ý là việc sử dụng của bạn emplace_backlà kỳ quặc: emplace_backlấy một danh sách các đối số và chuyển nó cho một nhà xây dựng cho std::thread. Bạn đã thông qua một ví dụ (giá trị) của std::thread, do đó sẽ xây dựng một luồng, sau đó di chuyển luồng đó vào vector. Hoạt động đó được thể hiện tốt hơn bằng phương pháp phổ biến hơn push_back. Sẽ hợp lý hơn khi viết threads.emplace_back([i](){ print_id(i); });(xây dựng tại chỗ) hoặc threads.push_back(std::thread([i](){ print_id(i); }));(xây dựng + di chuyển) có phần thành ngữ hơn.
Milo Brandt

Câu trả lời:


17

Các [&]cú pháp đang gây ra iđể được chụp bằng cách tham khảo . Vì vậy, khá thường xuyên do đó isẽ được nâng cao hơn nữa khi luồng chạy hơn bạn mong đợi. Nghiêm trọng hơn, hành vi của mã của bạn không được xác định nếu iđi ra khỏi phạm vi trước khi một luồng chạy.

Nắm bắt itheo giá trị - tức std::thread([i](){ print_id(i); })là sửa chữa.


2
Hoặc ít được sử dụng và không thường được khuyên dùngstd::thread([=](){ print_id(i); })
Wander3r

3
Hành vi đã không được xác định bởi vì đây là một cuộc đua dữ liệu trên (phi nguyên tử) ivới cách viết luồng chính và các luồng khác đang đọc.
quả óc chó

6

Hai vấn đề:

  1. Bạn không có quyền kiểm soát khi luồng chạy, điều đó có nghĩa là giá trị của biến itrong lambda có thể không như bạn mong đợi.

  2. Biến ilà cục bộ cho vòng lặp và chỉ vòng lặp. Nếu vòng lặp kết thúc trước khi một hoặc nhiều luồng chạy, các luồng đó sẽ có tham chiếu không hợp lệ đến một biến có thời gian kết thúc.

Bạn có thể giải quyết cả hai vấn đề này rất đơn giản bằng cách nắm bắt biến i theo giá trị thay vì tham chiếu. Điều đó có nghĩa là mỗi luồng sẽ có một bản sao của giá trị và bản sao đó sẽ được tạo thành duy nhất cho mỗi luồng.


5

Một điều nữa:
Đừng đợi cho đến khi luôn có một chuỗi được sắp xếp: 0, 1, 2, 3, ... bởi vì chế độ thực thi đa luồng có một đặc thù: không xác định .

Không xác định có nghĩa là việc thực hiện cùng một chương trình, trong cùng điều kiện, cho kết quả khác nhau.

Điều này là do hệ điều hành lập lịch xử lý các luồng khác nhau từ thực thi này sang thực thi khác tùy thuộc vào một số tham số: tải CPU, mức độ ưu tiên của các quy trình khác, có thể bị gián đoạn hệ thống, ...

Ví dụ của bạn chỉ chứa 5 luồng, vì vậy thật đơn giản, hãy thử tăng số lượng luồng và ví dụ đặt chế độ ngủ vào hàm xử lý, bạn sẽ thấy kết quả có thể khác nhau từ thực thi này sang thực thi khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.