2015-09-14 3 views
2

Каковы некоторые способы выполнения функции асинхронно, но для гарантии того, что она будет выполнена после возврата из текущей функции?std :: async гарантировать выполнение после оператора возврата

Пример:

std::future<void> future; 
    int do_smth(std::function<void()> callback) 
    { 
     int id = 0; 

     auto cb = [=](){ callback(id);}; 
     future = std::async(std::launch::async, cb); 

     return id; 
    } 

std::async вызова должен выполнить после возвращения было закончено. ASFAIK, что это не гарантировано, выполнение может все еще произойти до возвращения функции.

[Edit] Я хочу, чтобы это так я могу реализовать шаблон наблюдателя, например:

class A 
{ 
public: 
    void send_request() 
    { 
     m_id = do_smth(std::bind(&A::on_result,this,_1); 
    } 

    void on_result(RequestId id) 
    { 
     if (id == m_id) { 
      // my request finished! 
     } 
    } 
private: 
    RequestId m_id; 
}; 

Я знаю, что могу использовать нить, с очередью, и после событий, но думал, что может что-то проще , если бы я мог использовать std::async.

+0

Очевидным способом является создание потока в приостановленном состоянии и возобновление его после возврата функции. В настоящее время нет способа сделать это со стандартным C++, поэтому вам нужно будет сделать это в другой библиотеке потоков. – nwp

+1

Зачем вам это поведение? – GManNickG

+0

У меня возникло какое-то странное чувство, что вы пытаетесь реализовать асинхронный/ожидающий вид? Вещь –

ответ

2

Ваш пример будет заблокирован при вызове std::async и дождитесь завершения задачи async (так что это полностью синхронно).

Чтобы продолжить выполнение задачи, вам необходимо сохранить возвращенное будущее в переменной, которая переживает эту функцию, например. возвращая его из функции или назначений его в какой-то долгоживущий объект вне функции:

std::future<void> some_global_future; 

int do_smth(std::function<void()> callback) 
{ 
    int id = 0; 

    some_global_future = std::async(std::launch::async, callback, id); 

    return id; 
} 

Примечания нет необходимости создавать лямбда, как в вашем примере. Вы можете передать обратный вызов и его аргумент напрямую на std::async, и они будут скопированы и переданы в новый поток, что эквивалентно захвату [=], который вы использовали для лямбда.

Поскольку вы не появляетесь использовать future возвращаемый std::async другого варианта использовать отдельно стоящую нить:

int do_smth(std::function<void()> callback) 
{ 
    int id = 0; 

    std::thread(callback, id).detach(); 

    return id; 
} 

Update:, чтобы предотвратить новый поток работает, пока абонент не покинет вас будет нужна некоторая синхронизация, например

std::mutex mx; 

int do_smth(std::function<void()> callback) 
{ 
    int id = 0; 
    std::lock_guard<std::mutex> lock(mx); 
    auto cb = [=]{ std::lock_guard<std::mutex> l(mx); callback(id); }; 
    std::thread(cb).detach(); 

    return id; 
} 

Это гарантирует, что callback(id) не будет происходить до тех пор, пока мьютекс не может быть заблокирован, а это значит, do_smth должны быть возвращены.

Однако, что делает не означает, что назначение m_id будет завершена:

m_id = do_smth(std::bind(&A::on_result,this,_1); 

планировщик ОС может работать do_smth тогда, когда он возвращается разблокировать мьютекс, запустить асинхронный поток, а затем, когда это завершает работу с исходным потоком и назначает m_id.

У вас будет состояние гонки здесь из-за одного потока, назначающего m_id, в то время как поток, выполняющий обратный вызов, считывает его. Вам нужно установить m_id внутри do_smth, чтобы исправить это.

+0

обновил мой вопрос – yandreiy

+0

Итак, вы хотите, чтобы вы ** предотвращали ** асинхронную функцию даже начиная работать до тех пор, пока не вернется вызывающий? –

+0

Да, точно ... – yandreiy

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