Как безопасно использовать таймер дедлайна в нескольких потоках?

Я использую boost date_timer, как этот код:

boost::asio::io_service g_io;
#define DEL(x) {delete x; x = NULL;}

void thr1()
{
    for(;;)
    {

         ...
        boost::asio::deadline_timer *t1 = new boost::asio::deadline_timer(g_io, boost::posix_time::seconds(60));
        t1->async_wait(boost::bind(&callback, boost::asio::placeholders::error, t1));

         ...
    }
}


void thr2()
{
    for(;;)
    {
           ....


        boost::asio::deadline_timer *t2 = new boost::asio::deadline_timer(g_io, boost::posix_time::seconds(60));
        t2->async_wait(boost::bind(&callback, boost::asio::placeholders::error, t2));

            ....
    }
}


void thr3()
{
    for(;;)
    {

         ....

        boost::asio::deadline_timer *t3 = new boost::asio::deadline_timer(g_io, boost::posix_time::seconds(60));
        t3->async_wait(boost::bind(&callback, boost::asio::placeholders::error, t3));

         ....
    }
}


void thr4()
{
    for(;;)
    {
         ....

        boost::asio::deadline_timer *t4 = new boost::asio::deadline_timer(g_io, boost::posix_time::seconds(60));
        t4->async_wait(boost::bind(&callback, boost::asio::placeholders::error, t4));

        ....
    }
}

void io_work()
{
    boost::asio::io_service::work work(g_io);
    g_io.run();
}


int main()
{
    boost::thread thread1(thr1);
    boost::thread thread2(thr2);
    boost::thread thread3(thr3);
    boost::thread thread4(thr4);

    boost::thread service_thread(io_work);

    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();

    service_thread.join();

    return 0;

}


void callback(const boost::system::error_code& e, boost::asio::deadline_timer *timer)
{
    if(e)
    {
        std::cout << "cancel" << std::endl;
    }
    else
    {
        std::cout << " time out occurred" << std::endl;

        if(timer->expires_at() <= boost::asio::deadline_timer::traits_type::now())
        {  
            if(timer) DEL(timer);
            return;
        }

        timer->expires_at(timer->expires_at()+boost::posix_time::seconds(1));
        timer->async_wait(boost::bind(callback, boost::asio::placeholders::error, timer));
    }
}

Таймеры работают хорошо. Однако, когда возникают странные ошибки, я всегда сначала сомневался в своем asio-коде, потому что я не мог найти аналогичный asio-код, который я написал.

Могу ли я зарегистрировать таймер в нескольких потоках без мьютекса или синхронизации?

Кроме того, если вы обнаружите какие-либо проблемы в mycode, пожалуйста, сообщите мне.

Спасибо за чтение.


person Jongju Kim    schedule 05.03.2014    source источник
comment
В callback вы сначала удаляете timer, а затем вызываете его функции-члены expires_at и async_wait.   -  person Igor R.    schedule 05.03.2014
comment
Спасибо. На самом деле мой реальный код имеет оператор возврата. Я исправил свой код.   -  person Jongju Kim    schedule 05.03.2014


Ответы (2)


Экземпляры таймера крайнего срока не являются потокобезопасными.

Отдельные экземпляры являются потокобезопасными. Так что либо используйте отдельные таймеры, либо добавьте синхронизацию в доступ.

Ссылка на документацию:

person sehe    schedule 05.03.2014

Вы должны заметить, что ваша функция callback всегда будет вызываться в потоке, где вызывается io_service.run(), то есть service_thread в вашем случае. Поэтому вы получаете доступ к объектам таймера в двух потоках без синхронизации, что небезопасно без синхронизации.

Однако точная ситуация зависит от вашего полного кода (то, что находится в разделах ...). Если вы сделаете это точно как в вашем примере (только создав и запустив таймер в первом потоке (что гарантированно произойдет до обратного вызова) и получив к нему доступ только в потоке io_service, это должно быть безопасно .

person Matthias247    schedule 05.03.2014