2012-01-11 49 views
30

Trong C++ 11 có một loạt các công cụ tạo số ngẫu nhiên mới và các chức năng phân phối. Họ có an toàn không? Nếu bạn chia sẻ một phân phối ngẫu nhiên duy nhất và động cơ giữa nhiều chủ đề, nó có an toàn và bạn vẫn sẽ nhận được số ngẫu nhiên? Kịch bản Tôi đang tìm kiếm một cái gì đó như thế nào,C++ 11 An toàn chủ đề của các trình tạo số ngẫu nhiên

void foo() { 
    std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
#pragma omp parallel for 
    for (int i = 0; i < 1000; i++) { 
     double a = zeroToOne(engine); 
    } 
} 

sử dụng OpenMP hoặc

void foo() { 
    std::mt19937_64 engine(static_cast<uint64_t> (system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
    dispatch_apply(1000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) { 
     double a = zeroToOne(engine); 
    }); 
} 

sử dụng libdispatch.

Trả lời

25

Các thư viện chuẩn C++ 11 được rộng rãi chủ đề an toàn. Việc đảm bảo an toàn chủ đề trên các đối tượng PRNG giống như trên các thùng chứa. Cụ thể hơn, vì các lớp PRNG là tất cả pseudo -random, tức là chúng tạo ra một chuỗi xác định dựa trên trạng thái xác định hiện tại, thực sự không có chỗ để nhìn trộm hoặc chọc vào bất kỳ thứ gì bên ngoài trạng thái chứa (người dùng).

Cũng giống như các thùng chứa cần khóa để làm cho chúng an toàn để chia sẻ, bạn sẽ phải khóa đối tượng PRNG. Điều này sẽ làm cho nó chậm và không xác định. Một đối tượng trên mỗi luồng sẽ tốt hơn.

§17.6.5.9 [res.on.data.races]:

1 phần này quy định các yêu cầu mà việc triển khai phải đáp ứng để ngăn chặn các cuộc đua dữ liệu (1.10). Mỗi chức năng thư viện chuẩn sẽ là đáp ứng từng yêu cầu trừ khi có quy định khác. Việc triển khai có thể ngăn chặn các cuộc đua dữ liệu trong các trường hợp khác với các trường hợp được chỉ định bên dưới.

2 A C++ chức năng thư viện chuẩn có trách nhiệm không trực tiếp hoặc gián tiếp đối tượng truy cập (1.10) thể truy cập bằng đề khác hơn là thread hiện hành trừ các đối tượng được truy cập trực tiếp hoặc gián tiếp thông qua các tuyên bố argu- các chức năng, bao gồm này.

3 A C++ thư viện tiêu chuẩn chức năng sẽ không trực tiếp hoặc gián tiếp sửa đổi đối tượng (1.10) thể truy cập bằng đề khác hơn là thread hiện hành trừ các đối tượng được truy cập trực tiếp hoặc gián tiếp thông qua đối số const phi các chức năng, bao gồm này.

4 [Lưu ý: Điều này có nghĩa, ví dụ, rằng hiện thực không thể sử dụng một đối tượng tĩnh cho mục đích nội bộ mà không cần đồng bộ hóa vì nó thể gây ra một cuộc chạy đua dữ liệu ngay cả trong các chương trình mà không chia sẻ một cách rõ ràng đối tượng betweenthreads. -endnote]

5 A C++ thư viện tiêu chuẩn chức năng sẽ không truy cập vào đối tượng gián tiếp truy cập thông qua đối số của nó hoặc thông qua các yếu tố của vật chứa nó đối số trừ bằng cách gọi các chức năng theo yêu cầu của đặc điểm kỹ thuật của nó trên những yếu tố container.

6 Các thao tác trên các trình vòng lặp thu được bằng cách gọi một thư viện chuẩn chức năng thành viên chuỗi hoặc chuỗi có thể truy cập vào thùng chứa cơ bản, nhưng không được sửa đổi. [Lưu ý: Cụ thể, các hoạt động của container làm mất hiệu lực các trình vòng lặp xung đột với các hoạt động trên các trình lặp vòng được kết hợp với vùng chứa đó. - end note]

7 Việc triển khai có thể chia sẻ các đối tượng bên trong của chúng giữa các chủ đề nếu đối tượng không hiển thị với người dùng và được bảo vệ khỏi dữ liệu chủng tộc.

8 Trừ khi có quy định khác, chức năng thư viện chuẩn C++ sẽ thực hiện tất cả các thao tác chỉ trong chuỗi hiện tại nếu các hoạt động có hiệu ứng hiển thị (1.10) cho người dùng.

9 [Lưu ý: Điều này cho phép thực hiện song song hoạt động nếu không có tác dụng phụ rõ ràng. - lưu ý cuối cùng]

+0

Đó là cơ bản những gì tôi figured nó không phải là thread-an toàn. Có ok để chia sẻ các đối tượng phân phối 'std :: uniform_real_distribution zeroToOne (0.0, 1.0)' số lượng chủ đề và sử dụng một động cơ cho mỗi chủ đề? – user1139069

+0

@ user1139069: Không, không an toàn. Mặc dù ngay từ cái nhìn đầu tiên một đối tượng phân phối * có thể * thực hiện công việc của nó bằng cách ủy nhiệm mỗi cuộc gọi đến đối tượng động cơ, mà không duy trì trạng thái bên trong, nếu bạn nghĩ về nó thì động cơ không tạo ra đủ số bit ngẫu nhiên có thể cần phải được gọi hai lần. Nhưng hai lần (hoặc một lần) có thể là quá mức cần thiết, do đó, có thể tốt hơn để cho phép lưu vào bộ nhớ đệm của các bit ngẫu nhiên dư thừa. §26.5.1.6 \t "Yêu cầu phân phối số ngẫu nhiên" cho phép điều này; đối tượng phân phối cụ thể có trạng thái thay đổi theo từng cuộc gọi. Do đó chúng nên được coi là một phần của động cơ cho mục đích khóa. – Potatoswatter

0

Các documentation không đề cập đến chủ đề an toàn, vì vậy tôi sẽ cho rằng họ là không chủ đề an toàn.

+12

Không được đề cập trên cppreference.com không làm cho nó không phải như vậy. – Potatoswatter

2

Tiêu chuẩn (cũng N3242) dường như không đề cập đến việc tạo số ngẫu nhiên là chủng tộc miễn phí (ngoại trừ rand không), vì vậy không phải vậy (trừ khi tôi bỏ sót một thứ gì đó). Bên cạnh đó có thực sự không có điểm trong việc có chúng threadsave, vì nó sẽ phải chịu một chi phí tương đối khổng lồ (so với thế hệ của các con số chính nó ít nhất), mà không thực sự chiến thắng bất cứ điều gì. Ngoài ra, tôi không thực sự thấy một lợi ích khi có một trình tạo số ngẫu nhiên, thay vì có một luồng cho mỗi luồng, mỗi luồng được khởi tạo khác nhau (ví dụ từ kết quả của một trình tạo khác hoặc id luồng hiện tại). Afterall bạn có lẽ không dựa vào máy phát điện tạo ra một chuỗi nhất định mỗi chạy anyways. Vì vậy, tôi sẽ viết lại mã của bạn như một cái gì đó như thế này (đối với openmp, không có đầu mối về libdispatch):

void foo() { 
    #pragma omp parallel 
    { 
    //just an example, not sure if that is a good way too seed the generation 
    //but the principle should be clear 
    std::mt19937_64 engine((omp_get_thread_num() + 1) * static_cast<uint64_t>(system_clock::to_time_t(system_clock::now()))); 
    std::uniform_real_distribution<double> zeroToOne(0.0, 1.0); 
    #pragma omp for 
     for (int i = 0; i < 1000; i++) { 
      double a = zeroToOne(engine); 
     } 
    } 
} 
+1

Trên thực tế, nếu cùng RNG được đọc từ các luồng khác nhau, bạn * không thể * dựa vào việc nhận được cùng một chuỗi số ngẫu nhiên ngay cả đối với một hạt giống cố định vì lập lịch có thể gây ra thứ tự truy cập khác nhau cho RNG từ các luồng khác nhau. . Vì vậy, đặc biệt là * nếu bạn cần các chuỗi số ngẫu nhiên có thể tái tạo, bạn không nên chia sẻ các RNG giữa các luồng. – celtschk

+0

@celtschk: Điều đó phụ thuộc vào cách một định nghĩa nhận được cùng một chuỗi. Tôi sẽ nói rằng một trong những sẽ nhận được cùng một trình tự (globaly), nó chỉ là các chủ đề sẽ thấy các phần khác nhau của nó với mỗi lần chạy. – Grizzly

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