2013-04-19 41 views
20

Tôi cần đặt cờ cho một chuỗi khác để thoát. Chủ đề khác kiểm tra cờ thoát theo thời gian. Tôi có phải sử dụng nguyên tử cho lá cờ hoặc chỉ là một bool đơn giản là đủ và tại sao (với một ví dụ về những gì chính xác có thể đi sai nếu tôi sử dụng đồng bằng bool)?Tôi có phải sử dụng nguyên tử <bool> cho biến bool "thoát" không?

#include <future> 
bool exit = false; 
void thread_fn() 
{ 
    while(!exit) 
    { 
     //do stuff 
     if(exit) break; 
     //do stuff 
    } 
} 
int main() 
{ 
    auto f = std::async(std::launch::async, thread_fn); 
    //do stuff 
    exit = true; 
    f.get(); 
} 

Trả lời

21

Tôi có phải sử dụng nguyên tử cho biến bool "thoát" không?

.

Hoặc sử dụng atomic<bool> hoặc sử dụng đồng bộ hóa thủ công qua (ví dụ) std::mutex. Chương trình của bạn hiện có chứa một cuộc đua dữ liệu , với một chủ đề có khả năng đọc biến trong khi một chuỗi khác đang viết. Đây là hành vi không xác định.

mỗi khoản 1.10/21 của C++ 11 Tiêu chuẩn:

Việc thực hiện một chương trình có chứa một chủng tộc dữ liệu nếu nó có chứa hai mâu thuẫn hành động trong chủ đề khác nhau, ít nhất một trong số đó không phải là nguyên tử, và không xảy ra trước cái kia. Mọi kết quả cuộc đua dữ liệu như vậy sẽ dẫn đến hành vi không xác định.

Định nghĩa về "mâu thuẫn" được đưa ra tại Khoản 1.10/4:

Hai đánh giá biểu hiện xung đột nếu một trong số họ sẽ thay đổi một vị trí bộ nhớ (1.7) và một trong những khác truy cập hoặc sửa đổi cùng một vị trí bộ nhớ.

+0

Tôi khó có thể thấy cách truy cập một 'bool' có thể là gì ngoài nguyên tử trong thực tế (mặc dù tôi đồng ý trong tiêu chuẩn chính thức, đó là một vấn đề khác). Dù sao, ngoài điều kiện chủng tộc, tôi tin rằng người ta cũng cần một rào cản bộ nhớ (được cung cấp bởi 'nguyên tử <>' hoặc 'std :: mutex') để đảm bảo trình biên dịch không lưu trữ dữ liệu hoặc sắp xếp lại hướng dẫn. Tuy nhiên tôi thiếu kiến ​​thức lý thuyết để giải thích một cách chính xác (mặc dù tôi biết cách sử dụng nó trong thực tế), chăm sóc để khai sáng cho tôi nếu bạn có thể? Hay tôi nên tạo một câu hỏi mới? – syam

+14

Để xây dựng (hẹp), điều kiện chạy dữ liệu trong tiêu chuẩn không chỉ là về những gì một luồng có thể thực hiện được với chuỗi khác. Đó cũng là về tối ưu hóa hoặc sắp xếp lại mã. Trình biên dịch có thể giả định không có chủng tộc dữ liệu, vì vậy nó có thể giả định rằng nếu một luồng đọc một biến nhưng không sửa đổi nó, giá trị không thể thay đổi giữa các điểm đồng bộ hóa. Chủ đề của bạn có thể không bao giờ thoát. Hoặc nếu một chuỗi đặt cờ mà không sử dụng nó, sửa đổi có thể được sắp xếp lại trước bất kỳ mã nào khác (sau điểm đồng bộ hóa cuối cùng). Tôi thậm chí còn bỏ qua sự kết hợp bộ nhớ cache. Đây là lý do tại sao nguyên tử tồn tại. –

+0

@Andy Trích dẫn chính xác đến mức - đủ để tôi sử dụng nguyên tử . Hy vọng rằng Pete trong câu trả lời của ông sẽ làm rõ mô tả các vấn đề thực tế trong mã ví dụ của tôi có thể phát sinh từ việc sử dụng bool đơn giản. – PowerGamer

11

Có, bạn phải có một số đồng bộ hóa. Cách dễ nhất là, như bạn nói, với atomic<bool>.

Chính thức, như @AndyProwl nói, định nghĩa ngôn ngữ nói rằng không sử dụng nguyên tử ở đây mang lại hành vi không xác định. Có lý do chính đáng cho điều đó.

Đầu tiên, việc đọc hoặc ghi một biến có thể bị gián đoạn nửa chừng bởi một công tắc chủ đề; các chủ đề khác có thể thấy một giá trị bằng văn bản một phần, hoặc nếu nó thay đổi giá trị, các chủ đề ban đầu sẽ thấy một giá trị hỗn hợp. Thứ hai, khi hai luồng chạy trên các lõi khác nhau, chúng có bộ đệm riêng biệt; viết một giá trị lưu trữ nó trong bộ nhớ cache, nhưng không cập nhật bộ đệm khác, do đó, một chuỗi có thể không thấy giá trị được viết bởi một chuỗi khác. Thứ ba, trình biên dịch có thể tổ chức lại mã dựa trên những gì nó thấy; trong mã ví dụ, nếu không có gì bên trong vòng lặp thay đổi giá trị của exit, trình biên dịch không có bất kỳ lý do nào để nghi ngờ rằng giá trị sẽ thay đổi; nó có thể biến vòng lặp thành while(1).

Atomics giải quyết cả ba vấn đề này.

+1

Bạn có thể giải thích chi tiết câu trả lời của mình thêm một chút không: Vấn đề chính xác 1 có thể là vấn đề với biến bool chỉ với hai giá trị: 0 hoặc 1 (không có giá trị "nửa")? Vấn đề thứ hai: là bạn nói rằng các thread viết vào "thoát" var có thể ghi vào bộ nhớ cache và giá trị từ bộ nhớ cache sẽ không bao giờ đi vào bộ nhớ nơi "thoát" var nằm? Vấn đề thứ ba: bạn có nghĩa là trình biên dịch có thể (thông minh về mặt lý thuyết) rất thông minh để thấy rằng a) trước khi gọi tới thread_fn() "exit" luôn đúng và b) không có gì mà thread_fn() gọi thay đổi "exit" - quyền thay đổi vòng lặp, đúng không? – PowerGamer

+1

@PowerGamer - có, rách của một biến bool là khó xảy ra. Tôi sẽ không đi qua các kịch bản sai lầm mà nó giả định có thể xảy ra. Đối với các loại khác, nó có thể và sẽ xảy ra nếu bạn không thực hiện các bước để ngăn chặn nó. –

-1

thực sự, không có gì sai với bool đơn giản trong ví dụ cụ thể này. thông báo duy nhất là khai báo biến bool exit như volatile để giữ nó trong bộ nhớ. cả kiến ​​trúc CISC và RISC đều thực hiện lệnh đọc/ghi bool như hướng dẫn xử lý nguyên tử nghiêm ngặt.các bộ xử lý đa nhân hiện đại cũng có bộ triển khai bộ nhớ cache thông minh nâng cao. vì vậy, bất kỳ rào cản bộ nhớ nào cũng không cần thiết. Trích dẫn tiêu chuẩn không thích hợp cho trường hợp cụ thể này bởi vì nó chỉ xử lý một văn bản và chỉ đọc từ một luồng duy nhất.

+1

x86 thậm chí không _define_ một loại bool, do đó, nó không thể là nguyên tử hoặc. – MSalters

Các vấn đề liên quan