2010-12-30 33 views
5

Giả sử tôi có một chức năng mà cố gắng để bảo vệ một bộ đếm toàn cầu sử dụng mã này:Có phải xây dựng đối tượng phạm vi tĩnh đối tượng an toàn không?

static MyCriticalSectionWrapper lock; 
lock.Enter(); 
counter = ++m_counter; 
lock.Leave(); 

LÀ có một cơ hội mà hai luồng sẽ gọi constructor 's lock? Cách an toàn để đạt được mục tiêu này là gì?

+0

Xem thêm http://stackoverflow.com/questions/55510/when-do-function-level-static-variables-get-allocated-initialized –

Trả lời

4

Việc tạo đối tượng khóa không phải là chủ đề an toàn. Tùy thuộc vào trình biên dịch, bạn có thể có nhiều đối tượng khóa độc lập được tạo nếu nhiều luồng nhập hàm tại (gần) cùng một lúc.

Các giải pháp cho vấn đề này là sử dụng:

  • OS đảm bảo một thời gian intialization (đối với đối tượng khóa)
  • Double-checked locking (Giả sử nó là an toàn đối với trường hợp cụ thể của bạn)
  • Một chủ đề an toàn singleton cho đối tượng khóa
  • Ví dụ cụ thể của bạn, bạn có thể sử dụng một sợi khóa an toàn (ví dụ, chức năng InterlockedIncrement() cho Windows) cho gia tăng và tránh khóa hoàn toàn
+0

Sau đó, để làm cho nó an toàn, tôi cần để bọc nó trong một phần quan trọng, dẫn đến cùng một vấn đề ... phải không? –

+1

Một singleton chủ đề an toàn khó viết hơn bạn nghĩ. Một giải pháp tốt hơn là loại bỏ toàn cầu và sử dụng một đối tượng hoạt động thay thế. Đối tượng hoạt động sau đó sẽ chịu trách nhiệm về bất kỳ hành động nào bạn định làm với toàn cầu. – wilhelmtell

+5

AFAIK, Khóa kiểm tra kép không được coi là an toàn chỉ. Đó là loại chống mẫu. –

-1

Điều đó tùy thuộc vào việc triển khai khóa của bạn.

+0

Vui lòng giải thích. Bạn có thể giả sử trình bao bọc đơn giản của CRITICAL_SECTION với 'Init()' trong hàm tạo, và 'Enter()' và 'Leave()' chỉ ủy nhiệm cho Windows API. Với một chỉ thị biên dịch có điều kiện để chi nhánh để triển khai hệ điều hành khác, nhưng đó không phải là vấn đề. –

+0

-1 Tôi không nghĩ rằng tuyên bố của bạn là chính xác. Việc khởi tạo (xây dựng) của 'khóa' được thực hiện khi hàm được gọi đầu tiên, nhưng nếu hai luồng vừa gọi hàm cùng lúc trước khi biến' lock' được khởi tạo, thì mỗi luồng có thể thành công trong việc tạo khóa riêng của nó. Sau đó, bất kể tính chính xác của việc triển khai khóa, cả hai luồng đều có thể thực thi mã quan trọng cùng một lúc. –

+0

@DanielTrebbien No. Nếu khóa được viết chính xác thì hai luồng sẽ ** không ** có thể nhận được cùng một lúc. Đó là toàn bộ điểm của khóa để bắt đầu, và nếu nó không hoàn thành nó thì nó không phải là một khóa mà là một lỗi. Một khóa C++ được viết đúng cách sẽ có được khi xây dựng và phát hành khi hủy. Sau đó, không cần cho các lệnh gọi 'lock.Enter()' và 'lock.Leave()' tiếp theo. Đó là RAII. Nếu ctor khóa không đạt được thì nó sẽ ném. Các ctor phải có một phần nguyên tử để làm việc này. – wilhelmtell

1

Gọi trình xây dựng có thể được thực hiện và/hoặc môi trường thực thi phụ thuộc, nhưng đây không phải là một scoped_lock do đó không phải là một vấn đề.

Hoạt động chính được bảo vệ đúng cách chống lại truy cập đa luồng mà tôi nghĩ.

(Bạn biết đấy, toàn cầu cho toàn cầu, chức năng tĩnh cho chức năng tĩnh Đó biến khóa phải được xác định trong phạm vi cùng với các đối tượng được bảo vệ..)

+0

"biến khóa phải được xác định trong cùng phạm vi với đối tượng được bảo vệ." - đó là một nguyên tắc tốt. Cảm ơn bạn. –

0

gốc mẫu mã:

static MyCriticalSectionWrapper lock; 
lock.Enter(); 
counter = ++m_counter; 
lock.Leave(); 

tôi nhận ra rằng mã truy cập có lẽ chỉ là một trình giữ chỗ, tuy nhiên nếu nó thực sự là những gì bạn đang cố gắng làm, bạn có thể sử dụng chức năng Windows "InterlockedIncrement()" để thực hiện điều này. Ví dụ:

// atomic increment for thread safety 
InterlockedIncrement(&m_counter); 
counter = m_counter; 
+0

hoặc std :: nguyên tử những ngày này – paulm

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