2014-12-17 22 views
14

Tôi đang viết một số mã đa luồng và sử dụng lời hứa/tương lai để gọi một hàm trên một chuỗi khác và trả về kết quả của nó. Để đơn giản, tôi sẽ xóa hoàn toàn phần luồng:Thực hiện lời hứa (có thể là void)

template <typename F> 
auto blockingCall(F f) -> decltype(f()) 
{ 
    std::promise<decltype(f())> promise; 

    // fulfill the promise 
    promise.set_value(f()); 

    // block until we have a result 
    return promise.get_future().get(); 
} 

Điều này phù hợp với mọi chức năng trả về không void. Và tuyên bố trở lại nhận được tương lai cũng hoạt động cho void. Nhưng tôi không thể thực hiện lời hứa nếu f là một chức năng void, bởi vì:

promise.set_value (f()); // lỗi: sử dụng không hợp lệ của khoảng trống biểu

Có một số cách thông minh của thiết lập các giá trị trong trường hợp void nội dòng, hay tôi phải chỉ cần viết một hàm helper như call_set_value(promise, f) rằng có quá tải cho std::promise<R>std::promise<void>?

+0

Tôi không có kỹ năng mẫu để tự mình làm, nhưng tôi có cảm giác rằng bạn có thể SFINAE trên loại trả về. – Borgleader

+2

Điều gì chính xác là điểm gọi hàm trên một luồng khác nếu bạn chỉ đợi cho đến khi cuộc gọi được thực hiện? (Chặn các trường hợp rõ ràng của gửi cuộc gọi đến một * cụ thể * thread.) – cdhowie

+4

Tại sao bạn sử dụng lời hứa mức thấp - bạn chỉ có thể sử dụng std :: tương lai –

Trả lời

11

Một promise chỉ là một loại cung cấp kết quả không đồng bộ. Thay vì một promise bạn có thể sử dụng một packaged_task mà kết thúc tốt đẹp một đối tượng có thể được gọi, tương tự như một std::function ngoại trừ việc gọi nó làm cho các kết quả có sẵn thông qua một future (và dĩ nhiên là nó xử lý chênh lệch giữa void và phi khoảng trống kết quả):

template <typename F> 
auto blockingCall(F f) -> decltype(f()) 
{ 
    std::packaged_task<decltype(f())()> task(std::move(f)); 

    task(); 

    // block until we have a result 
    return task.get_future().get(); 
} 

NB theo tiêu chuẩn hiện tại, mã này sẽ có cuộc đua dữ liệu nếu task()task.get_future() xảy ra trên các chuỗi riêng biệt (và do đó ban đầu của bạn sử dụng lời hứa), vì vậy bạn nên gọi get_future() trước khi giao nhiệm vụ cho chủ đề khác. Trong thực tế, nó nên được an toàn trên thực hiện thực tế và có một vấn đề thư viện (LWG 2412) để làm cho nó hợp lệ anyway.

+0

Tại sao tôi không thể chuyển tác vụ thành 'std :: function'? ví dụ. 'std :: function func = [t = std :: move (tác vụ)]() mutable {t(); }; 'đưa ra một lỗi về' tác vụ' là không thể sao chép được. – Barry

+1

'std :: function' có thể sao chép được, do đó, yêu cầu đối tượng đích của nó cũng có thể sao chép được (kiểu đích được xóa, vì vậy' hàm' không thể được sao chép chỉ khi nó kết thúc một đối tượng có thể sao chép được, vì mục tiêu có thể thay đổi động tại thời gian chạy). 'packaged_task' là chỉ di chuyển, vì vậy không thể được lưu trữ trong' hàm' –

+0

Ồ đúng! Mát mẻ. Tôi thích giải pháp này. Ngoài ra tôi rất vui vì bạn đã thay đổi chiếc mũ của mình từ chiếc mũ Spock, khiến cho chắc chắn bức ảnh một mắt của bạn thậm chí còn rùng mình hơn nữa :) – Barry

12

Có. Chức năng quá tải là giải pháp sạch:

set(promise, f); 

sau đó thực hiện set như các chức năng quá tải, như:

template<typename F, typename R> 
void set(std::promise<R> & p, F && f) //handle non-void here 
{ 
    p.set_value(f()); 
} 

template<typename F> 
void set(std::promise<void> & p, F && f) //handle void here 
{ 
    f(); 
    p.set_value(); 
} 
Các vấn đề liên quan