синхронизация потоков

допустим, у меня есть метод блокировки, вызовем Block ().

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

однако у меня есть другое условие.

Я хочу, чтобы вызов блока возвращался через 5 секунд вверху, в противном случае я хочу, чтобы основной поток знал, что вызов блока завершился неудачно, и чтобы выйти из рабочего потока.

какое было бы лучшее решение в этом сценарии?

Я думал примерно так: создать рабочий поток в рабочем потоке, чтобы создать объект таймера с 5 секундами, и в дополнение к вызову gettickcount до и после вызова Block и вычисления дельты.

Кроме того, я определю логическое значение IsReturned, указывающее, вернула ли уже функция Block. после вызова Block, чтобы установить значение true.

в соответствии с этим логическим значением в функции таймера я решаю, как действовать:

  1. если логическое значение истинно, я ничего не делаю.

  2. если логическое значение false, я могу поставить APC OnFailure в очередь или, возможно, сигнализировать о событии Sucess в основном потоке и принудительно выйти из рабочего потока (дело в том, что я не уверен, смогу ли я это сделать)

Кроме того, после возврата функции блока я проверяю, составляет ли дельта 5 секунд, и помещаю в очередь APC OnSucess. (вопрос в том, что выход из потока вызывающего абонента также отменяет таймер? потому что в основном после этого таймер бесполезен)

p.s - если я могу точно знать, что могу отменить рабочий поток в функции таймера, я не думаю, что мне даже нужен материал gettickcount.

Благодарность!


person Idan    schedule 16.12.2009    source источник
comment
Какой API потоковой передачи вы используете? Pthreads? Увеличить потоки?   -  person mch    schedule 16.12.2009
comment
что делает этот метод блокировки? не можете ли вы использовать порты завершения ввода-вывода Windows и SleepEX ()?   -  person Hassan Syed    schedule 16.12.2009


Ответы (3)


Я бы посоветовал использовать для этого библиотеку boost :: thread. Вы можете периодически проверять, можно ли присоединиться к блокирующему потоку (т. Е. Все еще работает), а затем прервать его через пять секунд. Затем вам нужно будет написать функцию блокировки для обработки этого прерывания и правильного выхода.

#include <boost/thread/thread.hpp>

void Block(void)
{
    //Do work and periodically call boost::this_thread::sleep()
    try
    {
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
    }
    catch(boost::thread_interrupted const&)
    {
        return;
    }
}

int main(int argc, char *argv[])
{
    boost::thread blockThread(Block); //If Block takes arguments, just add them as arguments to the constructor.
    time_t startTime = time(NULL);

    while(true)
    {
        if(blockThread.joinable() && (time(NULL) - startTime) > 5)
        {
            blockThread.interrupt();
        }
        //Do whatever you want while waiting for the thread to finish.
    }
}

Изменить: проверьте документацию Управление потоками для больше точек прерывания и определение класса ускоряющих потоков.

Edit2: если вам не нужно выполнять какую-либо работу в основном потоке, ожидая завершения блокирующего потока, и нет удобного места для обработки прерываний в Block(), вы можете явно убить поток примерно так:

void Block(void)
{
    //Do work
}

int main(args)
{
    boost::thread blockThread(Block);

    //timed_join() returns false if the thread is still running after the specified time.
    if(!blockThread.timed_join(boost::posix_time::milliseconds(5000)))
    {   //detach() will kill the thread, any memory initialised in Block() will not be freed, any locals may or may not be freed either.
        blockThread.detach();
    }
}
person Ephphatha    schedule 16.12.2009

Я думаю, что вы примерно правы, хотя вы, вероятно, хотите, чтобы WM_TIMER сообщения доставлялись в основной поток, а не в потенциально блокирующий поток. В противном случае сообщения таймера могут быть потеряны, если поток блокируется до срабатывания таймера! Точно так же проверяйте прошедшее время в основном потоке, а не в рабочем, поскольку, если Block() блокируется, он не вернется, и вызов GetTickCount() после Block() никогда не произойдет.

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

В общем, удаление заблокированного потока может быть опасным. Документация Java категорически предостерегает от этого, и, во всяком случае, проблемы еще хуже с C ++. Считайте себя предупрежденным!

person Ben Karel    schedule 16.12.2009

Первоначальное создание потоков - дорогостоящая вещь, поэтому выполнение каждого вызова Block может быть не очень хорошей идеей.

Во-вторых, есть множество способов решить эту проблему, и это также сильно зависит от вашей среды. Например, в Windows возможный способ сделать это - создать рабочий поток с очередью сообщений. Затем вы определяете пару сообщений, которые обрабатываете в своем рабочем потоке. Один может быть WM_CALLBLOCK, другой может быть WM_AREYOUREADY и WM_YESIAM, когда вы хотите вызвать Block (), вы можете просто отправить это сообщение в рабочий поток, и он вызовет функцию. С сообщением вы также можете передать любые параметры, которые вам нужны для Block (). Поскольку ваша функция блокирует - если вы затем отправите сообщение WM_AREYOUREADY, вы не получите ответа WM_YESIAM напрямую. Так что вы можете построить свой тайм-аут на этом.

person AndersK    schedule 16.12.2009