Истекающий импульс boost :: stable_timer, работающий в потоке - PullRequest
0 голосов
/ 21 марта 2020

Моя цель - запустить несколько потоков, используя один io_context и steady_timer для выполнения множества задач. Когда задачи будут завершены, я хочу, чтобы срок действия steady_timer истек, и все потоки были остановлены.

В качестве теста у меня есть следующее:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using namespace boost::asio;

void print(const boost::system::error_code& /*e*/, boost::asio::steady_timer* t, int* count)
{
    std::cout << "every 2 seconds i count " << (*count)++ << " counts\n";

    t->expires_at(t->expiry() + chrono::seconds(2));
    t->async_wait(boost::bind(print, boost::asio::placeholders::error, t, count));
}

int main()
{
    int count = 0;
    io_context my_context;

    steady_timer my_timer(my_context, std::chrono::seconds(2));
    my_timer.async_wait(boost::bind(print, boost::asio::placeholders::error, &my_timer, &count));

    std::thread thread1( [&my_context]() {my_context.run(); } );

    Sleep(20000);

    my_timer.expires_at(my_timer.expiry());                                                         // expire after last action
    my_timer.async_wait(boost::bind(print, boost::asio::placeholders::error, &my_timer, &count));   // wait for it to expire        // doesn't work?
    thread1.join();                                                                                 // doesn't work?

    return 0;
}

У меня есть функция обработчика steady_timer с именем print, который имеет обратный вызов к своему собственному объекту steady_timer, t. Я хочу создать свой steady_timer объект и передать его в мою функцию-обработчик, затем выполнить некоторую задачу в функции-обработчике и затем истечь steady_timer, когда я закончу.

Однако, похоже, что истечение срока действия моего steady_timer объекта не позволяет моему потоку join(). Есть идеи, почему это так?

1 Ответ

1 голос
/ 21 марта 2020

Вы написали в комментарии, что cancel будет отменять инициацию асинхронной операции, но expires_at делает то же самое (см. ссылка , Все ожидающие асинхронные операции ожидания будет отменено ).

Asyn c Операция, запущенная в thread1 по async_write, может быть отменена при вызове expires_at из потока main. Этот лог c просто неверен, также, если вы хотите сделать это, вы должны использовать mutex для защиты одновременного доступа к steady_timer (общий экземпляр не является поточно-ориентированным - (см. reference ), Общие объекты: небезопасно. ).

Единственный безопасный способ остановить бесконечную цепочку вызовов функции print - добавить к функции print некоторое условие остановки.

Вы можете заключить steady_timer в структуру с atomic<bool> finish переменной-членом. По истечении 20 секунд вы устанавливаете этот элемент и проверяете его в функции print при обработке последнего вызова обработчика. Таким образом, вы уверены, что будет вызван весь запущенный обработчик.

struct Foo {
    boost::asio::steady_timer timer;
    std::atomic<bool> finish{false};

    template<class Expire>
    Foo(boost::asio::io_context& io, Expire expire) : timer(io, expire) {}
};

void print(const boost::system::error_code& e, Foo* foo, int* count) {
    if (foo->finish)
        return;
    std::cout << "every 2 seconds i count " << (*count)++ << " counts\n";
    foo->timer.expires_at(foo->timer.expiry() + chrono::seconds(2));
    foo->timer.async_wait(boost::bind(print, boost::asio::placeholders::error, foo, count));
}

int main() {
    int count = 0;
    io_context my_context;

    Foo foo(my_context, std::chrono::seconds(2));
    foo.timer.async_wait(boost::bind(print, boost::asio::placeholders::error, &foo, &count));

    std::thread thread1( [&my_context]() {my_context.run(); } );
    Sleep(20000); // 20 s
    foo.finish = true;
    thread1.join();       

Другое решение - добавить дополнительный параметр в функцию print, например, с именем: bool asTheLastOne. Каждый раз, когда вы запускаете async_write с print, вы решаете, будет ли эта операция последней.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...