2012-04-17 1 views
11

Я использую vc2011, и получается, что std :: async (std :: launch :: async, ...) немного глючит (иногда он не порождает новые потоки и запускает их параллельно, но вместо этого повторно использует потоки и запускают задачу один за другим). Это слишком медленно, когда я выполняю дорогостоящие сетевые вызовы. Поэтому я решил, что напишу свою собственную асинхронную функцию. Я застрял, хотя, где должно быть std :: обещать вживую? В 1) функция потока, 2) асинхронная функция или 3) функция вызывающего абонента.Замена std :: async с собственной версией, но где должно быть std :: prom live?

Код:

#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; 
    } 

} 

Я не могу показаться, чтобы пройти мимо того факта, что станд :: обещание нужно пережить функции асинхронной (и жить до тех пор, как нить), так что обещание Я не могу жить как локальная переменная в функции async. Но std :: prom тоже не должен жить в коде вызывающего абонента, так как вызывающему нужно знать только о фьючерсах. И я не знаю, как сделать обещание жить в функции потока, поскольку async нужно вернуть будущее, прежде чем он даже вызовет функцию fun. Я почесываю голову над этим.

У кого-нибудь есть идеи?

Редактировать: Я подчеркиваю это здесь, поскольку верхний комментарий немного дезинформирован. Хотя по умолчанию для std :: asycn разрешен режим отмены, когда явно задана политика запуска std :: launch :: async, она должна вести себя так, как будто «потоки порождаются и запускаются сразу» (см. Формулировку в en .cppreference.com/ж/CPP/резьба/асинхронный). См. Пример в pastebin.com/5dWCjjNY для одного случая, когда это не поведенческое поведение, наблюдаемое в vs20011. Решение отлично работает и ускорило мое приложение в реальном мире в 10 раз.

Редактировать 2: MS исправил ошибку. Более подробную информацию здесь: https://connect.microsoft.com/VisualStudio/feedback/details/735731/std-async-std-launch-async-does-not-behave-as-std-thread

+9

«Иногда он не порождает новые потоки и запускает их параллельно, но вместо этого повторно использует потоки и запускает задачу один за другим». Это не глючит; вот как это разрешено работать. В спецификации нет гарантии, что какой-либо конкретный асинхронный вызов будет работать в другом потоке от предыдущих или будущих асинхронных вызовов.Если вы этого хотите, просто создайте кучу потоков, вставьте их в контейнер и присоедините их, когда захотите вернуть данные. –

+1

В будущем, пожалуйста, разместите свой код непосредственно в своем вопросе, а не ссылайтесь на внешний сайт. – ildjarn

+0

@Nicol. Ты уверен? Как я понимаю при использовании std :: launch :: async, он должен действовать так, как если бы поток был порожден. Согласно http://en.cppreference.com/w/cpp/thread/async «Если политика и std :: launch :: async! = 0 (бит асинхронизации установлен), генерирует новый поток выполнения, как если бы std :: thread (f, args ...), за исключением того, что если функция f возвращает значение или генерирует исключение, она сохраняется в состоянии общего доступа, доступном через std :: future, который async возвращает вызывающему ». – petke

ответ

21

Вот одно решение:

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; 
} 

Выделяют обещание в куче, а затем передать по значению [=] shared_ptr к нему до лямбда.

+2

Wow ! Это работает. Если бы я мог, я бы дал вам миллион очков. Вы сделали мою неделю. – petke

5

Вам нужно перенести обещание в новую резьбу. Ответ Эндрю Томазоса делает это, создавая std::promise с общим достоянием, так что оба потока могут владеть обещанием, а когда текущий возвращается из текущей области, только новый поток владеет обещанием, т. Е. Передача права собственности. Но std::promise является подвижным, поэтому его можно перенести непосредственно в новый поток, за исключением того, что «очевидное» решение его захвата не работает, потому что лямбда не может захватить движением, только копией (или ссылкой, которая не будет работать, так как вы получите оборванную ссылку.)

Однако std::thread поддерживает передачу объектов rvalue в функцию запуска нового потока. так что вы можете объявить лямбда взять std::promise аргумент по значению, т.е. передать promise к лямбда, а не захватить его, а затем переместите обещание в один из аргументов std::thread например

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; 
} 

Это переместить обещают в объект std::thread, который затем перемещает его (в контексте нового потока) в параметр лямбда p.

 Смежные вопросы

  • Нет связанных вопросов^_^