2010-05-24 17 views
7

Nếu tôi đang truy cập một kiểu số nguyên (ví dụ: dài, int, bool, v.v ...) trong nhiều chủ đề, tôi có cần sử dụng cơ chế đồng bộ hóa hay không mutex để khóa chúng. Sự hiểu biết của tôi là như các loại nguyên tử, tôi không cần phải khóa truy cập vào một chủ đề duy nhất, nhưng tôi thấy rất nhiều mã ra khỏi đó mà không sử dụng khóa. Việc lập lại mã như vậy cho thấy rằng có một hiệu suất đáng kể cho việc sử dụng khóa, vì vậy tôi không muốn. Vì vậy, nếu mục tôi truy cập tương ứng với số nguyên chiều rộng của bus (ví dụ 4 byte trên bộ xử lý 32 bit), tôi có cần khóa truy cập vào nó khi nó đang được sử dụng trên nhiều luồng không? Đặt một cách khác, nếu luồng A đang ghi vào biến số nguyên X cùng lúc với chuỗi B đang đọc từ cùng một biến, có thể là chuỗi B có thể kết thúc một vài byte của giá trị trước đó được trộn lẫn với một vài byte giá trị được viết? Có phải phụ thuộc vào kiến ​​trúc này không, ví dụ: ok cho 4 số nguyên byte trên hệ thống 32 bit nhưng không an toàn trên số nguyên 8 byte trên hệ thống 64 bit?Tôi có cần sử dụng khóa với số nguyên trong chuỗi C++

Chỉnh sửa: Chỉ cần nhìn thấy điều này related post giúp bit công bằng.

Trả lời

7

Bạn không bao giờ khóa giá trị - bạn đang khóa một thao tác BẬT một giá trị.

C & C++ không đề cập rõ ràng chủ đề hoặc hoạt động nguyên tử - vì vậy các hoạt động giống như chúng có thể hoặc phải là nguyên tử - không được đảm bảo bởi đặc tả ngôn ngữ là nguyên tử. Nó sẽ được thừa nhận là một trình biên dịch khá deviant mà quản lý một nguyên tử đọc trên một int: Nếu bạn có một hoạt động mà đọc một giá trị - có lẽ có lẽ không cần phải bảo vệ nó. Tuy nhiên, nó có thể không nguyên tử nếu nó mở rộng ranh giới từ máy.

Thao tác đơn giản như m_counter++ liên quan đến hoạt động tìm nạp, tăng và lưu trữ - một điều kiện chủng tộc: một chuỗi khác có thể thay đổi giá trị sau khi tìm nạp trước cửa hàng - và do đó cần được bảo vệ bằng mutex - HOẶC tìm thấy trình biên dịch hỗ trợ cho các hoạt động liên khóa. MSVC có chức năng như _InterlockedIncrement() sẽ tăng vị trí bộ nhớ một cách an toàn miễn là tất cả các ghi khác tương tự bằng cách sử dụng apis liên khóa để cập nhật vị trí bộ nhớ - đó là các đơn đặt hàng có cường độ nhẹ hơn so với việc gọi một phần quan trọng.

GCC có các chức năng nội tại như __sync_add_and_fetch cũng có thể được sử dụng để thực hiện các hoạt động liên khóa trên các giá trị từ máy.

+0

Cảm ơn vì điều này, InterlockedExchange có lẽ là chức năng mà tôi đang tìm kiếm, chỉ có một chủ đề thực sự ghi vào biến được đề cập, trong khi những người khác chỉ đọc nó. –

4

Không có hỗ trợ cho các biến nguyên tử trong C++, vì vậy bạn cần khóa. Không có khóa, bạn chỉ có thể suy đoán về hướng dẫn chính xác sẽ được sử dụng cho thao tác dữ liệu và liệu những hướng dẫn đó có đảm bảo truy cập nguyên tử không - đó không phải là cách bạn phát triển phần mềm đáng tin cậy.

+4

Tôi phải không đồng ý (phần nào). Không có biến nguyên tử nào trong C++, không, nhưng cũng không có khóa nào. Ngay sau khi bạn nhận được vào đa luồng, bạn phải dựa vào các đảm bảo được đưa ra bởi trình biên dịch cụ thể của bạn. Và * rằng * không có nhiều đảm bảo cho nguyên tử. Nói chung, truy cập vào các đối tượng có kích thước từ sẽ là nguyên tử. Tất nhiên, ngôn ngữ C++ không đảm bảo điều này, nhưng trình biên dịch cụ thể của bạn có thể làm được. Tất nhiên, trình biên dịch thường đảm bảo về sắp xếp lại, vì vậy bạn vẫn có thể cần khóa, hoặc ít nhất là các rào cản bộ nhớ, tùy thuộc vào chính xác những gì bạn đang làm – jalf

3

Có thể sử dụng đồng bộ hóa sẽ tốt hơn. Mọi dữ liệu được truy cập bởi nhiều luồng phải được đồng bộ hóa.

Nếu đó là nền tảng cửa sổ, bạn cũng có thể kiểm tra tại đây: Interlocked Variable Access.

2

Có. Nếu bạn đang sử dụng Windows, bạn có thể xem các hàm/biến số Interlocked và nếu bạn là người thuyết phục Tăng cường thì bạn có thể xem implementation of atomic variables của chúng.

Nếu tăng quá nặng, hãy đặt "atomic c++" vào công cụ tìm kiếm yêu thích của bạn sẽ cung cấp cho bạn nhiều thức ăn để suy nghĩ.

3

Trong 99,99% trường hợp, bạn phải khóa, ngay cả khi đó là truy cập vào các biến dường như nguyên tử.Kể từ khi trình biên dịch C++ không nhận thức được đa luồng trên cấp độ ngôn ngữ, nó có thể làm được rất nhiều việc sắp xếp lại không tầm thường.

Trường hợp tại điểm: Tôi bị cắn bởi việc triển khai khóa xoay khi mở khóa chỉ đơn giản là gán 0 cho biến số nguyên volatile. Trình biên dịch được sắp xếp lại hoạt động mở khóa trước khi hoạt động thực tế dưới khóa, không ngạc nhiên, dẫn đến tai nạn bí ẩn.

Xem:

  1. Lock-Free Code: A False Sense of Security
  2. Threads Cannot be Implemented as a Library
+1

Tôi không ngạc nhiên chút nào :) Sắp xếp lại là khó chịu ở đây ... –

+0

Trình biên dịch BUG. dễ bay hơi là ranh giới tối ưu hóa. – Joshua

+2

@Joshua, Tôi có cảm giác mỉa mai không? Nếu không, thì không, nó không phải là lỗi trình biên dịch.Không có gì ngăn cản trình biên dịch sắp xếp lại màu đỏ không bay hơi/ghi xung quanh các biến động dễ bay hơi, nó chỉ không thể sắp xếp lại các lần đọc/ghi dễ bay hơi giữa chúng. Xem câu hỏi này: http://stackoverflow.com/questions/2535148/volatile-qualifier-and-compiler-reorderings –

3

Nếu bạn đang ở trên một máy tính với nhiều hơn một lõi, bạn cần để làm những điều đúng đắn mặc dù viết của một số nguyên là nguyên tử. Các vấn đề là hai lần:

  1. Bạn cần phải ngừng trình biên dịch tối ưu hóa ghi thực tế! (Hơi quan trọng này. ;-))
  2. Bạn cần các rào cản bộ nhớ (không phải những thứ được mô hình hóa trong C) để đảm bảo các lõi khác chú ý đến thực tế là bạn đã thay đổi mọi thứ. Nếu không, bạn sẽ bị rối trong bộ đệm giữa tất cả các bộ vi xử lý và các chi tiết bẩn khác như thế.

Nếu đó chỉ là điều đầu tiên, bạn sẽ được OK với đánh dấu biến volatile, nhưng thứ hai thực sự là kẻ giết người và bạn sẽ chỉ thực sự thấy sự khác biệt trên một máy đa lõi. Điều đó xảy ra là một kiến ​​trúc đang trở nên phổ biến hơn rất nhiều so với trước đây… Rất tiếc! Thời gian để ngừng bị cẩu thả; sử dụng đúng mã mutex (hoặc đồng bộ hóa hoặc bất kỳ thứ gì) cho nền tảng của bạn và tất cả chi tiết về cách làm cho bộ nhớ hoạt động như bạn tin rằng nó sẽ biến mất.

2

Đa luồng rất khó và phức tạp. Số lượng khó chẩn đoán vấn đề có thể xảy ra là khá lớn. Đặc biệt, trên các kiến ​​trúc intel đọc và ghi từ các số nguyên liên kết 32 bit được đảm bảo là nguyên tử trong bộ xử lý, nhưng điều đó không có nghĩa là an toàn để làm như vậy trong môi trường đa luồng.

Nếu không có bộ phận bảo vệ thích hợp, trình biên dịch và/hoặc bộ xử lý có thể sắp xếp lại các hướng dẫn trong khối mã của bạn. Nó có thể lưu các biến trong sổ đăng ký và chúng sẽ không hiển thị trong các chủ đề khác ...

Việc khóa là tốn kém và có các cấu trúc dữ liệu ít khóa khác nhau để tối ưu hóa hiệu năng cao, nhưng khó thực hiện đúng. Và vấn đề là một lỗi đồng thời thường che khuất và khó gỡ lỗi.

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