2013-06-24 67 views
5

Những gì tôi muốn làm nên khá dễ dàng, nhưng tôi không ...Sử dụng chức năng thành viên trong std :: packaged_task

Tất cả những gì tôi muốn làm là bắt đầu một hàm thành viên của một lớp trong nền tại một số thời điểm nhất định. Kết quả của hàm đó cũng phải là "bên ngoài" có sẵn. Vì vậy, tôi muốn chuẩn bị nhiệm vụ trong hàm tạo (thiết lập biến tương lai, ...) và bắt đầu nó ở một số điểm sau này.

Tôi đã cố gắng kết hợp std: :(​​packaged_task | async | future) nhưng tôi không làm cho nó hoạt động.

đoạn này sẽ không biên dịch, nhưng tôi nghĩ rằng nó cho thấy những gì tôi muốn làm:

class foo { 
private: 
    // This function shall run in background as a thread 
    // when it gets triggered to start at some certain point 
    bool do_something() { return true; } 

    std::packaged_task<bool()> task; 
    std::future<bool> result; 

public: 
    foo() : 
    task(do_something), // yes, that's wrong, but how to do it right? 
    result(task.get_future()) 
    { 
    // do some initialization stuff 
    ..... 
    } 
    ~foo() {} 

    void start() { 
    // Start Task as asynchron thread 
    std::async as(std::launch::async, task); // Also doesn't work... 
    } 

    // This function should return the result of do_something 
    bool get_result() { return result.get(); } 
}; 

Cảm ơn trước!

+0

'std :: async' là hàm, không phải loại –

Trả lời

8

Chỉ cần sử dụng std::bind():

#include <functional> // For std::bind() 

foo() : 
    task(std::bind(&foo::do_something, this)), 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
    result(task.get_future()) 
{ 
    // ... 
} 

Hơn nữa, bạn đang làm điều sai trái ở đây:

std::async as(std::launch::async, task) 
//   ^^ 
//   Trying to declare a variable? 

Kể từ khi những gì bạn muốn là để gọi std::async() chức năng, chứ không phải để khai báo một đối tượng của một (không tồn tại) loại std::async(). Vì vậy, như một bước đầu tiên, thay đổi này vào:

std::async(std::launch::async, task) 

Thông báo, tuy nhiên, điều này sẽ không đủ để có được những nhiệm vụ chạy không đồng bộ: vì những hành vi kỳ lạ của std::async() khi tương lai trở lại được loại bỏ, nhiệm vụ của bạn sẽ luôn được thực hiện như thể bạn bắt đầu đồng bộ hóa - hàm hủy của đối tượng tương lai được trả về sẽ chặn cho đến khi hoàn thành thao tác. (*)

Để giải quyết vấn đề cuối cùng này, bạn có thể giữ tương lai trở lại trong result biến thành viên của bạn (chứ không phải gán cho result tương lai được trả về bởi std::packaged_task::get_future() khi xây dựng):

result = std::async(std::launch::async, task); 
// ^^^^^^^^ 

(*) Tôi nghĩ rằng rằng MSVC bỏ qua đặc tả này và thực thi tác vụ không đồng bộ. Vì vậy, nếu bạn đang làm việc với VS2012, bạn có thể không gặp phải vấn đề này.

EDIT:

As correctly mentioned by Praetorian in his answer, bên trên vẫn sẽ là vấn đề, vì một bản sao của packaged_task sẽ cố tại một số điểm trong việc thực hiện các async(). Để giải quyết vấn đề này, bạn bọc đối tượng task của bạn trong trình bao bọc tham chiếu bằng cách sử dụng std::ref().

+0

Tuyệt vời, cảm ơn. Điều đó hoạt động. Nhưng làm cách nào để tôi bắt đầu tác vụ không đồng bộ? std :: async as (std :: launch :: async, task) vẫn không hoạt động – rralf

+1

@rralf: Đó là vì (1) bạn khai báo một đối tượng, không gọi hàm, và (2) vì một khối lạ hành vi của 'async() 'khi bạn không sử dụng tương lai được trả về. Chỉ cần làm 'auto f = async (...)'. Xem câu trả lời đã chỉnh sửa :) –

+0

Tôi nghĩ rằng khối mã cuối cùng là sai. Loại bỏ 'như'. – 0x499602D2

4

do_something() là một hàm thành viên, điều này có nghĩa là cần có con trỏ this tiềm ẩn làm đối số đầu tiên. Bạn sẽ cần phải bind con trỏ this hoặc tạo lamda gọi do_something.

foo() : 
    task(std::bind(&foo::do_something, this)), 
    result(task.get_future()) 
{} 

hoặc

foo() : 
    task([this]{ return do_something(); }), 
    result(task.get_future()) 
{} 

std::async as(std::launch::async, task); 

std::async là một hàm mẫu, không phải là một kiểu. Vì vậy, sự thay đổi rõ ràng là

std::async(std::launch::async, task); 

Nhưng điều đó gây ra thêm một lỗi vì đâu đó trong ruột của mà gọi một bản sao của task đang cố gắng, nhưng std::packaged_task có một constructor sao chép xóa. Bạn có thể khắc phục điều đó bằng cách sử dụng std::ref, điều này sẽ tránh được bản sao.

std::async(std::launch::async, std::ref(task)); 
Các vấn đề liên quan