Совместное использование ресурсов потока

Я борюсь с многопоточным программированием...

У меня есть приложение, которое взаимодействует с внешним устройством через модуль CAN-USB. У меня приложение отлично разговаривает по CAN-шине, но есть требование, чтобы приложение каждую секунду передало сообщение «сердцебиение».

Похоже, это идеальное время для использования потоков, поэтому я создал поток, который просыпается каждую секунду и отправляет сердцебиение. Проблема, с которой я столкнулся, заключается в совместном использовании интерфейса CAN-шины. Такт должен быть отправлен только тогда, когда шина простаивает. Как поделиться ресурсом?

Вот псевдокод, показывающий, что у меня есть до сих пор:

TMainThread
{
    Init:
        CanBusApi =new TCanBusApi;
        MutexMain =CreateMutex( "CanBusApiMutexName" );

        HeartbeatThread =new THeartbeatThread( CanBusApi );

    Execution:
        WaitForSingleObject( MutexMain );
        CanBusApi->DoSomething();
        ReleaseMutex( MutexMain );
}

THeartbeatThread( CanBusApi )
{
    Init:
        MutexHeart =CreateMutex( "CanBusApiMutexName" );

    Execution:
        Sleep( 1000 );
        WaitForSingleObject( MutexHeart );
        CanBusApi->DoHeartBeat();
        ReleaseMutex( MutexHeart );
}

Проблема, которую я вижу, заключается в том, что при вызове DoHeartBeat основной поток блокируется в ожидании MutexMain, как и ожидалось, но DoHeartBeat также останавливается. DoHeartBeat не завершается до тех пор, пока не истечет время ожидания WaitForSingleObject(MutexMain) из-за сбоя.

Выполняется ли DoHeartBeat в контексте MainThread или HeartBeatThread? Кажется, он выполняется в MainThread.

Что я делаю не так? Есть ли способ лучше?

Спасибо, Дэвид


person David    schedule 23.03.2010    source источник
comment
Давид, ты прав. Это я был виноват.   -  person    schedule 23.03.2010


Ответы (2)


Я подозреваю, что API CAN-шины является однопоточным. Возможно, ваш запрос DoHeartBeat() направляется из вашего второго потока обратно в основной поток. В этом случае у него не будет возможности добиться успеха, поскольку ваш основной поток заблокирован. Вы можете исправить это в основном двумя способами: (1) отправить сообщение в основной поток, сказав ему, чтобы он сделал сердцебиение, а не делать это во втором потоке; или (2) использовать таймер в основном потоке для вашего сердцебиения вместо второго потока. (Я действительно думаю, что многопоточность является излишним для этой конкретной проблемы.)

person Peter Ruderman    schedule 23.03.2010
comment
Я думаю, что это правда, что API шины CAN является однопоточным внутри и действует так, как будто DoHeartBeat() выполняется в контексте MainThread, но я не уверен, как это доказать. Верно ли, что методы объекта выполняются в потоке, создавшем объект, даже если они вызываются из другого потока? - person David; 23.03.2010
comment
Моей мотивацией для использования потоков является упрощение конечного автомата процесса. Поскольку пульс обрабатывается в отдельном потоке, мой конечный автомат может игнорировать строгие требования к времени пульсации. Если я использую один поток, я должен создать механизм, чтобы знать, когда шина используется, что и делает мьютекс. Казалось, лучше не реализовывать эту концепцию повторно. - person David; 23.03.2010
comment
Когда вы вызываете объектный метод, метод выполняется в контексте вызвавшего его потока, но это ничего не говорит о том, что метод будет делать. Если API-интерфейс CAN-шины реализован с использованием COM-сервера с потоковой передачей в квартире, то под прикрытием DoHeartBeat() отправит оконное сообщение в ваш основной поток и даст ему указание выполнить работу. Вам придется провести некоторое исследование, если это так. Хорошей новостью является то, что если API использует COM-сервер с апартаментами, то вам вообще не нужен мьютекс. - person Peter Ruderman; 23.03.2010

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

Если требуется фактическое сообщение пульса, и оно требуется каждую секунду, в приведенном выше коде должен быть только один мьютекс, и оба потока должны его совместно использовать. Написанный код создает два отдельных мьютекса, поэтому ни один из них не будет блокироваться. Вы столкнетесь с коллизией на канале, и в CanBusApi произойдут плохие вещи. Сделайте MainMutex видимой глобальной/классовой переменной и сделайте так, чтобы оба потока ссылались на нее.

person jfawcett    schedule 23.03.2010
comment
Сердцебиение требуется каждую секунду, независимо от трафика шины. Он удерживает принимающее устройство в тестовом режиме. - person David; 23.03.2010
comment
Я работаю в MS Windows. Насколько я понимаю, CreateMutex() создает новый мьютекс только в том случае, если он не находит его с указанным именем. В противном случае он возвращает дескриптор существующего мьютекса. Я ошибаюсь? - person David; 23.03.2010
comment
Нет, ты прав. Двойной вызов CreateMutex с одним и тем же именем возвращает два дескриптора одного и того же объекта. - person Peter Ruderman; 23.03.2010
comment
Тогда пересечение 1-секундного лимита является проблемой. Если вы можете гарантировать, что все они займут менее 1 секунды, отправьте HB до и после каждого сообщения, независимо от того, сколько времени это заняло, и по-прежнему иметь 1-секундный поток. В 1-секундном потоке время ожидания должно составлять 1 секунду — время, затраченное на ожидание. Вы будете рассылать спам HB-сообщениями, но не пропустите 1-секундный кроссовер, как в приведенном выше коде. - person jfawcett; 23.03.2010