2013-01-05 34 views
6

Tôi chỉ đọc bài viết 'Futures Done Right', và điều chính mà C++ 11 hứa hẹn đang thiếu dường như là việc tạo ra tương lai ghép từ những cái hiệncomposability tương lai, và đẩy mạnh :: wait_for_all

Tôi đang tìm ngay bây giờ tại các tài liệu của boost::wait_for_any

nhưng xem xét các ví dụ sau:

int calculate_the_answer_to_life_the_universe_and_everything() 
{ 
    return 42; 
} 

int calculate_the_answer_to_death_and_anything_in_between() 
{ 
    return 121; 
} 

boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything); 
boost:: future<int> fi=pt.get_future(); 
boost::packaged_task<int> pt2(calculate_the_answer_to_death_and_anything_in_between); 
boost:: future<int> fi2=pt2.get_future(); 

.... 


int calculate_the_oscillation_of_barzoom(boost::future<int>& a, boost::future<int>& b) 
{ 
    boost::wait_for_all(a,b); 
    return a.get() + b.get(); 
} 

boost::packaged_task<int> pt_composite(boost::bind(calculate_the_oscillation_of_barzoom, fi , fi2)); 
boost:: future<int> fi_composite=pt_composite.get_future(); 

Điều gì là sai với cách tiếp cận này để composability? đây có phải là cách hợp lệ để đạt được tính tương thích không? chúng ta cần một số edulcorant cú pháp thanh lịch trên mô hình này?

Trả lời

5

when_anywhen_all là cách hoàn toàn hợp lệ để soạn tương lai. Cả hai đều tương ứng với thành phần song song, trong đó hoạt động tổng hợp chờ một hoặc tất cả các phép toán được tạo thành.

Chúng tôi cũng cần bố cục tuần tự (không có trong Boost.Thread). Điều này có thể là, ví dụ, một hàm future<T>::then cho phép bạn xếp hàng hoạt động sử dụng giá trị của tương lai và chạy khi tương lai sẵn sàng. Có thể thực hiện điều này cho mình, nhưng với một sự cân bằng hiệu quả. Herb Sutter nói về điều này trong số recent Channel9 video của mình.

N3428 là một đề xuất dự thảo để thêm các tính năng này (và hơn thế nữa) vào thư viện chuẩn C++. Đó là tất cả các tính năng thư viện và không thêm bất kỳ cú pháp mới nào vào ngôn ngữ. Ngoài ra, N3328 là đề xuất thêm cú pháp cho chức năng có thể tiếp tục (như sử dụng async/await trong C#) sẽ sử dụng future<T>::then nội bộ.

+0

chúng tôi đã có các chức năng có thể tiếp tục làm thư viện tăng: xem 'boost :: context'. việc thực hiện một thư viện 'when_all' sẽ là một số biến thể mẫu variadic của ví dụ mã tôi đã đăng? hoặc là có một số phép thuật mà tôi đang bỏ lỡ? – lurscher

+0

Tất nhiên, tất cả những packaged_tasks cần phải được gửi tại một số điểm đến một hồ bơi thread hoặc 'asio :: io_service' để thực thi async, nhưng ngữ nghĩa không nên bị ảnh hưởng nếu/khi các tính toán được thực hiện – lurscher

+0

@lurscher Thực hiện' when_any' chỉ trên các tính năng thư viện hiện có là có thể, nhưng không nhất thiết phải hiệu quả. Tôi không thể nhìn thấy bất kỳ cách nào để làm điều đó mà không sinh ra một số lượng lớn các chủ đề, ngăn chặn chúng trên tất cả, và chờ đợi cho một trong số họ thức dậy. Mặt khác, nếu hệ điều hành cung cấp một số hỗ trợ, có thể có khả năng thực hiện hiệu quả hơn nhiều cho một nhà cung cấp thư viện chuẩn. –

3

Điểm để sử dụng từ edulcorant. :)

Sự cố với mã mẫu của bạn là bạn đóng gói mọi thứ thành công việc, nhưng bạn không bao giờ lên lịch các tác vụ đó để thực thi!

int calculate_the_answer_to_life() { ... } 

int calculate_the_answer_to_death() { ... } 

std::packaged_task<int()> pt(calculate_the_answer_to_life); 
std::future<int> fi = pt.get_future(); 
std::packaged_task<int()> pt2(calculate_the_answer_to_death); 
std::future<int> fi2 = pt2.get_future(); 

int calculate_barzoom(std::future<int>& a, std::future<int>& b) 
{ 
    boost::wait_for_all(a, b); 
    return a.get() + b.get(); 
} 

std::packaged_task<int()> pt_composite([]{ return calculate_barzoom(fi, fi2); }); 
std::future<int> fi_composite = pt_composite.get_future(); 

Nếu vào thời điểm này tôi viết

pt_composite(); 
int result = fi_composite.get(); 

chương trình của tôi sẽ chặn vĩnh viễn. Nó sẽ không bao giờ hoàn tất, vì pt_composite bị chặn trên calculate_barzoom, được chặn trên wait_for_all, mà bị chặn trên cả fifi2, không ai trong số đó sẽ không bao giờ hoàn thành cho đến khi ai đó thực hiện pt hoặc pt2 tương ứng. Và không ai sẽ thực hiện chúng, bởi vì chương trình của tôi bị chặn!

Bạn có thể có nghĩa là tôi phải viết một cái gì đó như thế này:

std::async(pt); 
std::async(pt2); 
std::async(pt_composite); 
int result = fi_composite.get(); 

này sẽ làm việc. Nhưng nó cực kỳ kém hiệu quả - chúng tôi sinh ra ba luồng công nhân (thông qua ba cuộc gọi đến async), để thực hiện công việc của hai chủ đề. Chủ đề thứ ba - luồng đang chạy pt_composite - sẽ được sinh ra ngay lập tức và sau đó chỉ cần ngồi đó ngủ cho đến ptpt2 đã kết thúc chạy.Đó là tốt hơn so với quay nhưng thấp hơn đáng kể so với không tồn tại: điều đó có nghĩa là nhóm luồng của chúng tôi có ít nhân viên hơn mức cần phải có. Trong một cài đặt pool-pool hợp lý chỉ với một luồng cho mỗi lõi CPU, và rất nhiều nhiệm vụ đến trong mọi lúc, điều đó có nghĩa là chúng ta có một lõi CPU chỉ đang ngồi nhàn rỗi, bởi vì chuỗi công nhân là có nghĩa là đang chạy trên lõi đó hiện bị chặn bên trong wait_for_all.

Những gì chúng ta muốn làm là tuyên bố ý định của chúng tôi khai báo:

int calculate_the_answer_to_life() { ... } 

int calculate_the_answer_to_death() { ... } 

std::future<int> fi = std::async(calculate_the_answer_to_life); 
std::future<int> fi2 = std::async(calculate_the_answer_to_death); 
std::future<int> fi_composite = std::when_all(fi, fi2).then([](auto a, auto b) { 
    assert(a.is_ready() && b.is_ready()); 
    return a.get() + b.get(); 
}); 

int result = fi_composite.get(); 

và sau đó có thư viện và các công việc lên lịch với nhau để Do The Right Thing: không đẻ trứng bất kỳ sợi nhân mà có thể' t ngay lập tức tiến hành nhiệm vụ của nó. Nếu người dùng cuối phải viết ngay cả một dòng mã rõ ràng ngủ, chờ hoặc chặn, một số hiệu suất chắc chắn sẽ bị mất.

Nói cách khác: Không hiển thị chủ đề công nhân trước thời gian của nó.


Rõ ràng đó là thể để làm tất cả điều này trong tiêu chuẩn C++, không nơi nương tựa thư viện; đó là cách chính thư viện được triển khai! Nhưng đó là một nỗi đau rất lớn để thực hiện từ đầu, với nhiều cạm bẫy tinh tế; vì vậy đó là lý do tại sao nó là một điều tốt mà hỗ trợ thư viện dường như đến sớm.

Đề xuất ISO N3428 được đề cập trong Roshan Shariff's answer đã được cập nhật thành N3857N3865 cung cấp nhiều chức năng tiện lợi hơn nữa.