2012-02-08 31 views
34

Tôi muốn chuỗi của mình tắt hơn một cách duyên dáng vì vậy tôi đang cố gắng triển khai một cơ chế báo hiệu đơn giản. Tôi không nghĩ rằng tôi muốn có một chủ đề hoàn toàn hướng sự kiện vì vậy tôi có một nhân viên với một phương pháp để ngăn chặn nó graceully sử dụng một phần quan trọng Monitor (tương đương với một C# lock Tôi tin):Có thể đọc cờ boolean được chia sẻ mà không khóa nó khi một luồng khác có thể đặt nó (tối đa một lần) không?

DrawingThread.h

class DrawingThread { 
    bool stopRequested; 
    Runtime::Monitor CSMonitor; 
    CPInfo *pPInfo; 
    //More.. 
} 

DrawingThread.cpp

void DrawingThread::Run() { 
    if (!stopRequested) 
     //Time consuming call#1 
    if (!stopRequested) { 
     CSMonitor.Enter(); 
     pPInfo = new CPInfo(/**/); 
     //Not time consuming but pPInfo must either be null or constructed. 
     CSMonitor.Exit(); 
    } 
    if (!stopRequested) { 
     pPInfo->foobar(/**/);//Time consuming and can be signalled 
    } 
    if (!stopRequested) { 
     //One more optional but time consuming call. 
    } 
} 


void DrawingThread::RequestStop() { 
    CSMonitor.Enter(); 
    stopRequested = true; 
    if (pPInfo) pPInfo->RequestStop(); 
    CSMonitor.Exit(); 
} 

tôi hiểu (ít nhất là trong Windows) Monitor/lock s là đắt đồng bộ hóa thread nhất nguyên thủy nhưng tôi đang quan tâm để tránh lạm dụng. Tôi có nên gói từng đọc của cờ boolean này không? Nó được khởi tạo thành false và chỉ được đặt một lần thành true khi dừng được yêu cầu (nếu nó được yêu cầu trước khi tác vụ hoàn tất).

Gia sư của tôi khuyên bảo vệ thậm chí bool vì đọc/ghi có thể không phải là nguyên tử. Tôi nghĩ lá cờ này là ngoại lệ chứng tỏ quy tắc?

+1

Làm theo lời khuyên của gia sư (có thể là đúng) - ngay cả khi họ không phải là những người đánh dấu công việc của bạn, vì vậy nếu họ nói cần thiết thì cần. – John3136

+6

@ John3136 Lời khuyên của gia sư tôi là 10 tuổi, đây là công việc của tôi bây giờ. – John

Trả lời

38

Không bao giờ có thể đọc được nội dung nào đó có thể được sửa đổi trong một chuỗi khác mà không đồng bộ hóa. Mức độ đồng bộ hóa là cần thiết phụ thuộc vào những gì bạn đang thực sự đọc. Đối với các loại nguyên thủy, bạn nên xem qua số lần đọc nguyên tử, ví dụ: ở dạng std::atomic<bool>.

Đồng bộ hóa lý do luôn cần thiết là các bộ xử lý sẽ có dữ liệu có thể được chia sẻ trong một dòng bộ nhớ cache. Không có lý do gì để cập nhật giá trị này thành giá trị có thể thay đổi trong một chuỗi khác nếu không có đồng bộ hóa. Tệ hơn nữa, tuy nhiên, nếu không có đồng bộ hóa nó có thể viết sai giá trị nếu một cái gì đó được lưu trữ gần với giá trị được thay đổi và đồng bộ hóa.

+0

Tôi không nghĩ nền tảng của mình bao gồm 'std :: atomic'. – John

+1

'std :: atomic ' là một phần của C++ 2011. Nó thường được thực hiện trên cơ sở cung cấp của nền tảng đi kèm với bất cứ điều gì được chọn trên nền tảng tôn trọng. Hiệu quả nó bao gồm việc sử dụng một hoạt động xử lý thích hợp. Cách chúng được gọi và sử dụng phụ thuộc vào nền tảng. 'std :: atomic ' mang lại một giao diện đẹp và di động cho chúng. –

+0

Tôi có thể duy trì được khả năng truy cập với 'CSMonitor' nhiều hơn, như tôi đang tìm cách tránh. Vì vậy, tôi không thể bỏ qua điều này một cách an toàn, ngay cả với 'volatile bool stopRequested' vì giá trị đó có thể được lưu trữ trong CPU và không biết gì về cập nhật ngay cả thông qua accessor được giám sát,' RequestStop', trừ khi nó đang được đọc trong một phần quan trọng? – John

10

Phân công Boolean là nguyên tử. Đó không phải là vấn đề.

Vấn đề là một luồng có thể không thấy thay đổi đối với biến được thực hiện bởi một luồng khác nhau do trình biên dịch hoặc sắp xếp lại CPU hoặc bộ đệm dữ liệu (tức là luồng đọc cờ boolean có thể đọc giá trị được lưu vào bộ nhớ cache) của giá trị được cập nhật thực tế).

Giải pháp là hàng rào bộ nhớ, thực sự được thêm vào một cách rõ ràng bằng các báo cáo khóa, nhưng đối với một biến duy nhất, nó quá mức cần thiết. Chỉ cần khai báo nó volatile.

+1

"Phân công Boolean là nguyên tử": Chỉ cần không quan tâm, đây có phải là tiêu chuẩn C hoặc C++ không? –

+16

Lưu ý rằng việc khai báo một thứ 'biến động' trong C++ có ** không có ** hiệu lực khi đồng bộ ** ở tất cả **! Việc sử dụng 'volatile' là cho trình biên dịch để ngăn chặn nó bỏ qua và/hoặc sắp xếp lại các hướng dẫn (điều này khác với ví dụ về ngữ nghĩa của' volatile' trong Java). Bạn cũng cần nói với CPU rằng cần đồng bộ hóa. Bạn cần sử dụng chỉ thị tương ứng, ví dụ: 'std :: nguyên tử '. –

+2

@NiklasB. Tôi nghĩ điều gì có nghĩa là: bạn không thể kết thúc với một trạng thái không nhất quán của một 'bool' được nhìn thấy bởi một sợi chỉ. Nó thực sự là nguyên tử theo nghĩa này. Tuy nhiên, điều này không có nghĩa là giá trị được đồng bộ hóa theo bất kỳ cách nào với các lõi khác nhau. Đối với những nguyên tử này bạn cần đồng bộ hóa, ví dụ: sử dụng 'std :: nguyên tử '. –

1

Không, bạn phải bảo vệ mọi quyền truy cập, vì trình biên dịch hiện đại và cpus sắp xếp lại mã mà không có nhiệm vụ đa luồng của bạn. Truy cập đọc từ các luồng khác nhau có thể hoạt động, nhưng không phải làm việc.

4

Câu trả lời, tôi tin là "nó phụ thuộc". Nếu bạn đang sử dụng C++ 03, luồng không được định nghĩa trong tiêu chuẩn, và bạn sẽ phải đọc những gì trình biên dịch và thư viện chủ đề của bạn nói, mặc dù loại điều này thường được gọi là "cuộc đua lành tính" and is usually OK.

Nếu bạn đang sử dụng C++ 11, các cuộc đua lành tính là hành vi không xác định. Ngay cả khi hành vi không xác định không có ý nghĩa đối với loại dữ liệu cơ bản. Vấn đề là các trình biên dịch có thể giả định rằng các chương trình không có hành vi không xác định, and make optimizations based on that (xem thêm Phần 1 và Phần 2 được liên kết từ đó). Ví dụ, trình biên dịch của bạn có thể quyết định đọc cờ một lần và cache giá trị bởi vì nó là hành vi không xác định để ghi vào biến trong một luồng khác mà không có một loại rào cản bộ nhớ hoặc mutex nào đó.

Tất nhiên, cũng có thể là trình biên dịch của bạn hứa hẹn sẽ không thực hiện tối ưu hóa đó. Bạn sẽ cần phải nhìn.

Giải pháp dễ nhất là sử dụng std::atomic<bool> trong C++ 11 hoặc một cái gì đó như Hans Boehm's atomic_ops ở nơi khác.

+1

Nó không "phụ thuộc": nếu bạn cần chia sẻ dữ liệu có thể thay đổi trên nhiều luồng, ngay cả khi nó chỉ là 'bool', bạn cần một số loại đồng bộ hóa. Về cơ bản, điều này là để thông báo cho CPU sắp xếp các đối tượng đang được lưu vào bộ nhớ cache để hiển thị. –

+1

Nó phụ thuộc vào ý nghĩa rằng (1) ngay cả khi Tiêu chuẩn nói điều gì đó không xác định, việc triển khai ** được ** cho phép xác định điều gì sẽ xảy ra và các chủng tộc lành tính là đủ phổ biến mà có một cơ hội tốt mà trình biên dịch có thể làm như vậy; và (2) trong khi các cuộc đua lành tính chắc chắn không được xác định trong C++ 11, thì khó có thể nói rằng với C++ 03 (mặc dù bài báo đầu tiên tôi liên kết nói chính xác điều đó, tất nhiên nó cũng cho thấy sự phổ biến của các kiểu dữ liệu này) là). –

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