2012-12-19 24 views
16

Tôi đã đọc on Stackoverflow rằng không có thùng chứa STL nào an toàn chỉ để viết. Nhưng điều đó có nghĩa là gì trong thực tế? Nó có nghĩa là tôi nên lưu trữ dữ liệu ghi trong mảng đồng bằng?Chủ đề an toàn của việc viết std :: vectơ so với mảng đồng bằng

Tôi mong đợi các cuộc gọi đồng thời tới std::vector::push_back(element) có thể dẫn đến cấu trúc dữ liệu không nhất quán vì nó có thể dẫn đến việc thay đổi kích thước vectơ. Nhưng những gì về một trường hợp như thế này, nơi thay đổi kích thước không được tham gia:

1) sử dụng một mảng:

int data[n]; 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

2) sử dụng một 'std :: vector``:

std::vector<int> data; 
data.resize(n); 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

Việc triển khai đầu tiên có thực sự tốt hơn cái thứ hai a) về an toàn luồng và b) về hiệu suất không? Tôi muốn sử dụng một std :: vector, vì tôi ít thoải mái với các mảng kiểu C.

CHỈNH SỬA: Tôi đã xóa #pragma omp atomic update bảo vệ ghi.

+1

Tôi không chắc chắn đủ để làm cho nó là một câu trả lời, nhưng tôi khá chắc chắn bằng văn bản cho các yếu tố khác nhau của một 'std :: vector' là thread-safe. – Angew

+1

Hai đoạn mã này đều an toàn. –

+0

"Nhưng điều đó có nghĩa là gì trong thực tế?" : Nó có nghĩa là một thùng chứa phải được chốt độc quyền cho cả hai ghi * và * đọc nếu có/hoặc hoạt động trùng với một ** viết ** đồng thời. Bạn có thể có tất cả các độc giả đập vào một thùng chứa mà bạn muốn, nhưng ngay sau khi viết * tiềm năng * được giới thiệu, tất cả các phiên cược bị tắt và bạn phải chốt xuống * tất cả * truy cập (không chỉ các nhà văn khác). Khóa đơn ghi nhiều lần hoạt động tốt cho điều này, btw. – WhozCraig

Trả lời

22

Cả hai đều an toàn như nhau. Miễn là không có yếu tố được truy cập từ nhiều chủ đề bạn OK. Vòng lặp song song của bạn sẽ truy cập từng phần tử chỉ một lần và do đó chỉ từ một chuỗi.

Không gian trong tiêu chuẩn cho các chức năng thành viên của các thùng chứa không an toàn. Trong trường hợp này bạn sử dụng vector<int>::operator[], vì vậy bạn muốn đảm bảo rõ ràng về an toàn luồng cho thành viên đó, điều này có vẻ hợp lý vì gọi nó ngay cả trên một vectơ không const không sửa đổi chính vectơ. Vì vậy, tôi nghi ngờ rằng có một vấn đề trong trường hợp này, nhưng tôi đã không tìm kiếm sự bảo đảm [sửa: rici tìm thấy nó]. Ngay cả khi nó có khả năng không an toàn, bạn có thể làm int *dataptr = &data.front() trước vòng lặp và sau đó lập chỉ mục tắt dataptr thay vì data.

Ngoài ra, mã này là không phải được đảm bảo an toàn cho vector<bool>, vì đây là trường hợp đặc biệt mà nhiều thành phần cùng tồn tại bên trong một đối tượng. Nó sẽ được an toàn cho một mảng của bool, vì các yếu tố khác nhau của đó là khác nhau "vị trí bộ nhớ" (1.7 trong C++ 11).

+0

Tôi đã viết một chức năng kiểm tra nhỏ để xem liệu openmp có hoạt động chính xác hay không. Tôi đã sử dụng 'vector ', thay đổi kích thước nó thành số lượng các chủ đề được sử dụng (khởi tạo thành false), và đặt tất cả các giá trị thành true đồng thời. Nếu mọi giá trị là true, thì hàm trả về true để cho thấy rằng tất cả các luồng đều sinh ra chính xác. 'vector ' đã xảy ra lần nữa. May mắn thay, tôi thực sự đọc phần liên quan của tiêu chuẩn gần đây để tôi có thể tìm ra nó, nhưng nếu bất cứ ai tình cờ gặp vấn đề này, họ cần đảm bảo rằng họ tránh viết đồng thời với 'vector '. – Justin

+0

bằng cách này, nó sẽ được an toàn để push_back cho vectơ cá nhân dưới 'std :: vector >' từ mỗi chủ đề riêng biệt tương ứng? –

1

Điều chính có nghĩa là nếu bạn có nhiều luồng truy cập vectơ, bạn không thể phụ thuộc vào C++ để tránh làm hỏng cấu trúc dữ liệu với nhiều lần ghi đồng thời. Vì vậy, bạn cần phải sử dụng một số loại bảo vệ. Mặt khác, nếu chương trình của bạn không sử dụng nhiều chủ đề, như các ví dụ của bạn dường như không, bạn hoàn toàn ổn.

+5

Ví dụ của anh ta sử dụng nhiều luồng thông qua OpenMP – nogard

+2

Upvoted lên 0. Điều này có thể bị downvoted là không chính xác nhưng đã được đăng trước khi chỉnh sửa được xóa: #pragma omp cập nhật nguyên tử, ngăn chặn việc viết đồng thời và làm cho câu trả lời này đúng trong ngữ cảnh của bài viết gốc –

1

Trong trường hợp này, bạn chỉ nên tạo vectơ của mình với số lượng giá trị cần thiết? và tất cả sẽ hoạt động tốt.

std::vector<int> data(n, 0); 

resize() hoạt động tốt. Hiệu suất sẽ bằng nhau. Lý do tại sao truy cập đa luồng sẽ không làm hỏng vectơ là: dữ liệu của bạn được đặt tại vị trí của nó và sẽ không di chuyển từ đó. Các chuỗi OMP sẽ không truy cập vào cùng một phần tử tại một thời điểm.

17

Đối với C++ 11, trong đó quy định quy tắc cho cuộc đua dữ liệu, chủ đề an toàn của các thùng chứa được mô tả. Phần có liên quan của tiêu chuẩn là § 23.2.2, đoạn 2:

Bất kể (17.6.5.9), việc triển khai được yêu cầu để tránh các cuộc đua dữ liệu khi nội dung của đối tượng được chứa trong các phần tử khác nhau trong cùng một chuỗi, ngoại trừ vector <bool>, được sửa đổi đồng thời.

[Lưu ý: Đối với một véc tơ <int> x với kích thước lớn hơn một, x [1] = 5 và * x.begin() = 10 có thể được thực hiện đồng thời mà không có cuộc đua dữ liệu, nhưng x [0] = 5 và * x.begin() = 10 thực hiện đồng thời có thể dẫn đến một cuộc đua dữ liệu. Ngoại lệ cho quy tắc chung, đối với một véc tơ <bool> y, y [0] = true có thể chạy với y [1] = true. —end note]

Được đề cập § 17.6.5.9 về cơ bản cấm mọi sửa đổi đồng thời bởi bất kỳ giao diện thư viện chuẩn nào trừ khi được cho phép cụ thể (và bao gồm việc sử dụng của bạn).

Kể từ khi câu hỏi đã được nêu ra bởi Steve Jessop, khoản 1 § 23.2.2 cho phép một cách rõ ràng việc sử dụng đồng thời [] trong các thùng chứa chuỗi:

Đối với mục đích tránh các cuộc đua dữ liệu (17.6.5.9), triển khai sẽ xem xét các hàm sau là const: bắt đầu, kết thúc, rbegin, rend, trước, sau, dữ liệu, tìm, lower_bound, upper_bound, equal_range, tại và, ngoại trừ trong các tổ hợp liên kết hoặc không có thứ tự, toán tử [].