2012-04-17 19 views
11

Tôi đang sử dụng vc2011 và nó chỉ ra std :: async (std :: launch :: async, ...) là một chút lỗi (đôi khi nó không sinh ra các luồng mới và chạy chúng song song, nhưng thay vào đó reuses chủ đề và chạy nhiệm vụ cái khác). Điều này quá chậm khi tôi thực hiện các cuộc gọi mạng tốn kém. Vì vậy, tôi figured tôi muốn viết chức năng async của riêng tôi. Tôi đang gặp khó khăn mặc dù, nơi nên std :: lời hứa sống? Trong 1) chức năng chủ đề, 2) chức năng async, hoặc 3) chức năng người gọi.Thay thế std :: async với phiên bản của riêng mình nhưng nên std :: lời hứa sống ở đâu?

Code:

#include <future> 
#include <thread> 
#include <iostream> 
#include <string> 
#include <vector> 

std::string thFun() { 
    throw std::exception("bang!"); 
    return "val"; 
} 

std::future<std::string> myasync(std::promise<std::string>& prms) { 
//std::future<std::string> myasync() { 
    //std::promise<std::string> prms; //needs to outlive thread. How? 

    std::future<std::string> fut = prms.get_future(); 
    std::thread th([&](){ 
     //std::promise<std::string> prms; //need to return a future before... 
     try { 
      std::string val = thFun(); 

      prms.set_value(val); 

     } catch(...) { 
      prms.set_exception(std::current_exception()); 
     } 

    }); 

    th.detach(); 
    return fut; 
} 

int main() { 

    std::promise<std::string> prms; //I really want the promise hidden iway in the myasync func and not live here in caller code but the promise needs to outlive myasync and live as long as the thread. How do I do this? 
    auto fut = myasync(prms); 

    //auto fut = myasync(); //Exception: future already retrieved 

    try { 
     auto res = fut.get(); 
     std::cout << "Result: " << res << std::endl; 

    } catch(const std::exception& exc) { 
     std::cout << "Exception: " << exc.what() << std::endl; 
    } 

} 

I cant dường như vượt qua một thực tế là std :: hứa cần phải sống lâu hơn chức năng async (và sống lâu như sợi chỉ), do đó không thể hứa sống như một biến cục bộ trong func async. Nhưng std :: promise cũng không nên tồn tại trong mã người gọi, vì người gọi chỉ cần biết về tương lai. Và tôi không biết làm thế nào để làm cho lời hứa sống trong chức năng thread như async cần phải trả lại một tương lai trước khi nó thậm chí còn gọi func thread. Tôi đang gãi đầu vào cái này.

Bất kỳ ai có ý tưởng nào?

Chỉnh sửa: Tôi đang làm nổi bật điều này ở đây vì nhận xét hàng đầu có một chút sai lệch. Trong khi mặc định cho std :: asycn được cho phép là chế độ dererred, khi một chính sách khởi động của std :: launch :: async được thiết lập rõ ràng nó phải hoạt động "như thể" các luồng được sinh ra và chạy cùng một lúc (xem wording trong vi .cppreference.com/w/cpp/thread/async). Xem ví dụ trong pastebin.com/5dWCjjNY cho một trường hợp mà đây không phải là hành vi được nhìn thấy trong vs20011. Giải pháp hoạt động tốt và tăng tốc ứng dụng thế giới thực của tôi theo hệ số 10.

Chỉnh sửa 2: MS sửa lỗi. Xem thêm thông tin ở đây: https://connect.microsoft.com/VisualStudio/feedback/details/735731/std-async-std-launch-async-does-not-behave-as-std-thread

+9

"đôi khi nó không sinh ra chủ đề mới và chạy chúng song song, nhưng thay vì sử dụng lại chủ đề và chạy tác vụ cái khác" Đó không phải là lỗi; đó là cách nó được phép làm việc. Không có sự đảm bảo nào trong đặc tả rằng bất kỳ cuộc gọi async cụ thể nào sẽ chạy trong một luồng khác từ các cuộc gọi không đồng bộ trước hoặc trong tương lai.Nếu bạn muốn điều đó, sau đó chỉ cần tạo một loạt các chủ đề, dính chúng vào một thùng chứa, và sau đó tham gia chúng khi bạn muốn lấy lại dữ liệu. –

+1

Trong tương lai, vui lòng đặt mã của bạn trực tiếp trong câu hỏi của bạn thay vì liên kết đến một trang web bên ngoài. – ildjarn

+0

@Nicol. Bạn có chắc không? Theo tôi hiểu khi sử dụng std :: launch :: async nó sẽ hoạt động như thể một luồng được sinh ra. Theo http://en.cppreference.com/w/cpp/thread/async "Nếu chính sách & std :: launch :: async! = 0 (bit async được thiết lập), sinh ra một luồng mới thực hiện như thể bởi std :: thread (f, args ...), ngoại trừ nếu hàm f trả về một giá trị hoặc ném một ngoại lệ, nó được lưu trữ trong trạng thái chia sẻ có thể truy cập thông qua std :: future mà async trả về cho người gọi. " – petke

Trả lời

21

Dưới đây là một giải pháp:

future<string> myasync() 
{ 
    auto prms = make_shared<promise<string>>(); 

    future<string> fut = prms->get_future(); 

    thread th([=](){ 

     try { 
      string val = thFun(); 
      // ... 
      prms->set_value(val); 

     } catch(...) { 
      prms->set_exception(current_exception()); 
     } 

    }); 

    th.detach(); 

    return fut; 
} 

Phân bổ lời hứa trên heap, và sau đó pass-by-value [=] một shared_ptr với nó thông qua các lambda.

+2

Wow ! Điều đó hoạt động. Tôi sẽ cung cấp cho bạn một triệu điểm nếu tôi có thể. Bạn đã thực hiện tuần của tôi. – petke

5

Bạn cần chuyển lời hứa vào chuỗi mới. Câu trả lời của Andrew Tomazos thực hiện bằng cách tạo một std::promise với quyền sở hữu chia sẻ, để cả hai chủ đề có thể sở hữu lời hứa và khi chủ đề hiện tại trả về từ phạm vi hiện tại, chỉ chủ đề mới sở hữu lời hứa, tức là quyền sở hữu đã được chuyển. Nhưng std::promise có thể di chuyển để có thể di chuyển trực tiếp vào chuỗi mới, ngoại trừ việc giải pháp "rõ ràng" chụp ảnh không hoạt động vì lambda không thể chụp bằng cách di chuyển, chỉ bằng bản sao (hoặc tham chiếu, sẽ không hoạt động khi bạn nhận được một tham chiếu lơ lửng.)

Tuy nhiên, std::thread hỗ trợ truyền các đối tượng rvalue cho hàm bắt đầu của chuỗi mới. vì vậy bạn có thể tuyên bố lambda để có một lập luận std::promise theo giá trị, tức là vượt qua promise đến lambda chứ không phải chụp nó, và sau đó di chuyển hứa hẹn thành một trong những lập luận của std::thread ví dụ

std::future<std::string> myasync() { 
    std::promise<std::string> prms; 

    std::future<std::string> fut = prms.get_future(); 
    std::thread th([&](std::promise<std::string> p){ 
     try { 
      std::string val = thFun(); 

      p.set_value(val); 

     } catch(...) { 
      p.set_exception(std::current_exception()); 
     } 

    }, std::move(prms)); 

    th.detach(); 
    return fut; 
} 

này di chuyển lời hứa vào đối tượng std::thread, sau đó di chuyển nó (trong ngữ cảnh của chuỗi mới) vào tham số lambda p.