Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2007.05.06;
Скачать: [xml.tar.bz2];

Вниз

потоки   Найти похожие ветки 

 
Mixal__   (2006-12-06 07:26) [0]

Добрый день. Вот такой вопрос профессионалам:
Есть программа создающая отдельный thread, в котором идёт опрос устройства(обращение к COM порту посредством ReadFile/WriteFile) через промежутки времени (sleep). В основной программе также есть обращения к этому устройству. Дело в том, что устройство может обработать последующий запрос только спустя некоторое время(время обработки запроса).  Вызовы из потока могут наступить в любое время, когда ещё не обработан устройством вызов из главной программы. Каким образом можно исключить такую ситуацию?


 
Сергей М. ©   (2006-12-06 08:41) [1]


> Каким образом можно исключить такую ситуацию?
>


Синхронизировав обращения к ресурсу (СОМ-порту в твоем случае) со стороны более чем одного потока - см. крит.секции, мьютексы, семафоры

В целом же такая логика ничем не оправдана. С коммуникационным ресурсом (тем самым СОМ-портом) должен взаимодействовать только один тред - транспортный. Все остальные треды, заинтересованные во вводе/выводе с использованием этого ресурса, должны делать запросы транспортному треду на прием/передачу требуемых данных, ставя эти запросы в очередь. Транспортный тред выбирает эти запросы из очереди и исполняет их, формируя результаты и передавая их заинтересованному треду.


 
Kolan ©   (2006-12-06 08:45) [2]

> (sleep

Вот это для жадержек обращения вообще не используй. Источник ошибок. Они возникают на машинах с разной производительностью.


> Вызовы из потока могут наступить в любое время, когда ещё
> не обработан устройством вызов из главной программы.

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

Ожидать удобнос пом WaitFro функций, например WaitForSingleObject


 
Kolan ©   (2006-12-06 08:46) [3]

> Сергей М

:)


 
Mixal__   (2006-12-06 09:37) [4]

Не использовать поток не получится т.к делаю плугин dll для чужой программы...

Сделать работу с устр-вом в одном месте тоже врятли получится...
При инициализации dll создаётся поток в котором идёт опрос...
Также есть вызываемые в любой момент из dll функции, которым также необходимо работать с устройством...

Так... В функции передачи команды устройству использовал критическую секцию...
вначале функции
EnterCriticalSection( LockSection);

... тут работаю с портом ....

Sleep(150); // жду когда команду обработает устр-во гарантированно

LeaveCriticalSection(LockSection);

---- end;
выход ли это из положения?

И с чего Sleep должен глючить... этож функция виндов....


 
Орион ©   (2006-12-06 09:50) [5]

> выход ли это из положения?

неа.


> И с чего Sleep должен глючить... этож функция виндов....

:))


 
clickmaker ©   (2006-12-06 09:51) [6]


> И с чего Sleep должен глючить... этож функция виндов....

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


 
Сергей М. ©   (2006-12-06 10:01) [7]


> Mixal__


Что-то я не понял, каким образом создаваемый тобой поток умудряется открыть СОМ-порт, если он уже открыт в это время другим потоком ?


 
Kolan ©   (2006-12-06 10:07) [8]

> Sleep(150); // жду когда команду обработает устр-во гарантированно

Во-во, а теперь пойди на Pentium II и на Intel Duo Core и тои 150 превратятся в хз сколько...


> Сделать работу с устр-вом в одном месте тоже врятли получится...

Не в одном месте, а так сказать через одно место :).

Нужно сделать "Менеджер устр-ва".
Иесли кому-то  нужно обратиться к устройству, то он обращетеся к "Менеджеру".

А менеджер уже разбирается с портом и тд...


> Не использовать поток не получится

Так никто и не отговаривает делай пток для работы - это удобно.


 
Kolan ©   (2006-12-06 10:08) [9]

> При инициализации dll создаётся поток в котором идёт опрос...

Зачем опрос всегда? Только по событию.


 
Kolan ©   (2006-12-06 10:09) [10]

Ой, недописал...

> Также есть вызываемые в любой момент из dll функции, которым
> также необходимо работать с устройством...

Тут видимо очередь и будет.
У "Менеджер устр-ва"(или у кого то еще) должна быть очередь запросов.


 
C   (2006-12-06 10:33) [11]

Kolan ©   (06.12.06 10:07) [8]
>> Sleep(150); // жду когда команду обработает устр-во гарантированно

>Во-во, а теперь пойди на Pentium II и на Intel Duo Core и тои 150 превратятся в хз сколько...


Почему?


 
Kolan ©   (2006-12-06 10:40) [12]

Windows - не есть система реального времени.
Sleep(150) читать надо так:
-Дорогая Windows немогла бы ты усыпить поток на, примерно, 150 мс если конечно можно.
[Ответ системы] - Я конечно попытаюсь но ничего не обещаю

:)


 
Mixal__   (2006-12-06 10:40) [13]

Очередь запросов... значит юзать семафоры надо?
2Kolan ©
>Зачем опрос всегда? Только по событию.

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

2clickmaker ©
150мс это точно гарантированное время *5 обработки команды устройства   - т.к. устройство не "какое-то" а "своё"

2Сергей М. ©

>Что-то я не понял, каким образом создаваемый тобой поток умудряется >открыть СОМ-порт, если он уже открыт в это время другим потоком ?

Порт открывается при инициализации длл. В длл порт юзают из потока и из функций, вызываемых программой, подключившую длл.


 
Сергей М. ©   (2006-12-06 10:48) [14]


> Mixal__   (06.12.06 10:40) [13]


Тогда ничто не мешает использовать твой доп.поток как транспортный. Он будет заниматься и "слежением" и обслуживанием запросов прочих потоков на ввод/вывод


 
Kolan ©   (2006-12-06 10:57) [15]

> устройство пассивное - никогда не передаёт само данные,
> только по запросу

Отлично так и должно быть Запрос-ответ.

> 150мс это точно гарантированное время

Ну поверь что так делоть нендо.

значит юзать семафоры надо
Да что хочешь

Еще раз, алгоритм такой.
1. Есть "менеджер устройства", который реализует все доступные функции устройства.
2. У менеджера есть очереть(TQueue непример).
3. Кто угодно просит менеджера опросить устройство.
4. Он кладет запрос в очередь.
5. Если очередь не пуста он посылает запрос через порт и ждет ответ. Если ответ есть то все ок если нет - ошибка связи.

Пример:

Пусть у уср-ва 2 комманды:
1. Получить данные.
2. Получить версию прошивки.

И есть два потока:
1. Главный
2. Поток опроса.
3. Поток менеджера устр-ва

Поток опроса каждую сек. посылает запрос "Получить данные" и в это время пользователь нажалд кнопку "Спросить версию".

1. Поток в "менеджере устр-ва" смотрит что очередь пуста ниче не делает.
2. Поток "опроса" добавляет коммаду получить данные
3. Поток менеджера смотрит что очередь не пуста отправляет комманду через порт, ждет, если ответ получен возвращает его.
4. Поток "опроса" добавляет коммаду получить данные и так далее.
5. Вдруг пользователь жмет на конпку "Получить версию прошивки". И гл поток тоже добавляет комманду "Версия прошивки".

Те очередь будет такая:

Получить данные
Спровить версию
Получить данные


6. Ну х сним, поток менеджера знай себе выбирает комманды да отробатыавает их. Сначала он получит данные, затем спросит версию, затем опять данные.

Как видишь из разных мест и потоков я работаю с пом 1 единственного(его надо сделать синглетоном) объекта - "менеджера устр-ва".

Вот как-то так. Тук работы прилично... Асинхронизировать оч просто TCriticalSection.


 
Reindeer Moss Eater ©   (2006-12-06 11:06) [16]

Вызовы из потока могут наступить в любое время, когда ещё не обработан устройством вызов из главной программы. Каким образом можно исключить такую ситуацию?

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


 
C   (2006-12-06 11:07) [17]

Kolan ©   (06.12.06 10:40) [12]
Windows - не есть система реального времени.

Это понятно. Непонятно, с чего бы ей превращаться в хз, да еще и зависеть от тактовой частоты процессора.


 
Kolan ©   (2006-12-06 11:13) [18]

> [17] C   (06.12.06 11:07)
> Kolan ©   (06.12.06 10:40) [12]
> Windows - не есть система реального времени.
>
> Это понятно. Непонятно, с чего бы ей превращаться в хз,
> да еще и зависеть от тактовой частоты процессора.

Зависит много от чего. И от запушеных программ итд.. Да че уговаривать, делай так потом программу заказчику отдашь и повеселишся :). Я слава богу лично не обжигался, люди же уже все это прошли.


 
С   (2006-12-06 11:19) [19]

Я в курсе от чего и насколько оно зависит. И частота процессора в этот список если и входит, то в самой наименьшей степени


 
Mixal__   (2006-12-06 11:34) [20]

2Kolan ©
>> 150мс это точно гарантированное время

>Ну поверь что так делоть нендо.

Как же ещё сделать то? Как узнать, занято в данный момент устройство выполнением предыдущей посланной команды? да никак.... проще уж подождать немного и уже потом с уверенностью засылать следующую команду.

2Сергей М. ©

Как я Вас понял будет выглядеть примерно так...?

какой-то глобальный флаг имеющейся команды

var flag:bool=false;  

и некий "буфер" для хранения команды

var cmd:...

потом идут функции, которые вызываются программой

function Cmd1.(....):....;
begin
cmd = чтото
flag:=true;
end;
.
.
.

function CmdN.(....):....;
begin
cmd = чтото
flag:=true;
end;

затем в потоке

while(not terminated)do begin
 if(flag)then begin
  doCmd(cmd) ;  // послать команду
  flag:=false;
 end;
...
чтото - ещё
...

end;


 
C   (2006-12-06 11:44) [21]

Mixal__   (06.12.06 11:34) [20]
Если устройство свое, то кто мешает сделать так, чтобы оно извещало хост о завершении обработки полученной команды, если уж во время обработки нельзя отвлечься на запись в буфер передачи одного байта - влага "занято". Или все-таки можно?:)


 
Mixal__   (2006-12-06 11:57) [22]

2C

Извещать хосту устройство не может,т.к по своей инициативе не передаёт данные, только путём запрос/ответ.


 
Сергей М. ©   (2006-12-06 11:59) [23]


> Mixal__   (06.12.06 11:34) [20]


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

Трансп.тред выбирает из очереди запросов очередной запрос.
Элемент этой очереди = уник.идентификатор запроса + кто послал + команда + необходимо ли возвратить результат исполнения запроса).

Далее тред исполняет запрос и , если необходимо, помещает его результат в очередь результатов.
Элемент этой очереди = уник.идентификатор запроса  + собственно результаты его выполнения


 
С   (2006-12-06 12:22) [24]

Ну получило устройство новый запрос во время обработки предыдущего. Кто ему мешает тут же отправить ответ типа "отвали, я занято"? Опчть же, если устройство по результу обработки запроса выдает ответ, то кто мешает использовать сей факт как индикатор готовности. А если не выдает, то кто мешает заставить его ответ выдавать, раз устройство тобой и разработано. Впрочем, как знаещь


 
Mixal__   (2006-12-06 12:46) [25]

Вобщем смысл понятен... Но всётаки несовсем понятно, почему обязательно использовать механизм, при котором работа с портом будет происходить из одного потока...
Например я вставлю посылку/приём команды например в отдельную процедуру, в которой буду юзать критическую секцию:

1. EnterCriticalSection
2. WriteFile - пишу в порт
3. ReadFile - читаю ответ
4. sleep - подожду пока устройство подготовится к приёму новой команды
5. LeaveCriticalSection

Эта процедура будет вызываться из моего потока например раз в секунду...и вот она вызвалась.... во время нахождения в критической секции юзер жмёт кнопку и вызывается процедура получения прошивки, в которой есть вызов этой же самой функции, которая работает с портом, но она в данный момент занята и придётся подождать... прога ждёт выполнения... потом входит и работает с портом. и вроде всё ок...
Чтото не так?
И чем заменить sleep раз он такой глючный...


 
Сергей М. ©   (2006-12-06 13:03) [26]


> несовсем понятно, почему обязательно использовать механизм,
>  при котором работа с портом будет происходить из одного
> потока


Потому что децентрализация логики плоха сама по себе.


 
Mixal__   (2006-12-06 13:06) [27]

2C

Устройство выдаёт ответ после полной обработки команды, но дело в том, что устройство занимается не только обработкой запросов с ПК... Если придёт команда во время того, когда контроллер чемто занят - это не страшно - команда попадёт в буфер, но её обработка начнётся только тогда, когда пройдёт полный цикл работы устройства.  

Поэтому чтобы не "завалить" устройство запросами надо немного подождать.


 
Сергей М. ©   (2006-12-06 13:06) [28]


> Mixal__   (06.12.06 12:46) [25]


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


 
Mixal__   (2006-12-06 13:33) [29]

2Сергей М. ©

Поток нужен в любом случае т.к. необходим постоянный контроль устройства, с периодом не менее 1сек, а функции из длл вызываются только при необходимости, могут и вообще не вызываться в течении продолжительного времени.
А так как у меня не используются спец. линии для того-чтобы понять - готово устройство или нет, как например у модема, связь идёт только по линиям tx и rx, то ждать надо полюбому.

И всё-же чем заменить Sleep? - смотреть системный таймер миллисекунд и считать? Насколько я помню есть ф-я гдето в кернеле timeGetTime-возвращает сколько прошло мс после запуска винды- может воспользоваться ей?


 
Anatoly Podgoretsky ©   (2006-12-06 14:16) [30]

> Kolan  (06.12.2006 10:40:12)  [12]

Назови систему подлинного реального времени, только не с разной длиной поводка, а именно подлинную.


 
Anatoly Podgoretsky ©   (2006-12-06 14:18) [31]

> Mixal__  (06.12.2006 12:46:25)  [25]

> И чем заменить sleep раз он такой глючный...

Тебя обманули, ты пока новенькие и еще не знаешь кого не следует слушать.


 
Kolan ©   (2006-12-06 17:18) [32]

> Назови систему подлинного реального времени, только не с
> разной длиной поводка, а именно подлинную.

Незнаю. Знаю что Windows не из их числа.

> Тебя обманули, ты пока новенькие и еще не знаешь кого не
> следует слушать.

А это в мой огород камень. Тады пользуй Sleep для ожидания, пользуй.


> Как узнать, занято в данный момент устройство выполнением
> предыдущей посланной команды?

Оно само должно об этом уведомить...


 
Kolan ©   (2006-12-06 17:19) [33]

> поводка

Даже более того я незнаю что есть "поводок".


 
Anatoly Podgoretsky ©   (2006-12-06 21:26) [34]

> Kolan  (06.12.2006 17:19:33)  [33]

Поводок в данном случае - время реакции, у MS Windows оно 10 мс
Раньше такое время и не снилось.
Ни одна многозадачная системе не является системой реального времени, она только более или менее похожа на нее. Стандартная Виндоус не лучшее решение для построения систем реального времени, для этого больше подходят Embeded решения, начиная от НТ и до ХР как минимум.
Там можешь формировать ядро с точностью до функции.


 
Сергей М. ©   (2006-12-07 08:21) [35]


> Mixal__   (06.12.06 13:33) [29]
> Поток нужен в любом случае т.к. необходим постоянный контроль
> устройства


Контроль на предмет чего ?

Кстати вопрос на засыпку - при инициализации ты создаешь тред, а где, когда и как он у тебя уничтожается ?


 
Mixal__   (2006-12-07 14:07) [36]

2Сергей М. ©

Контроль - это опрос разных датчиков.

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

Поток уничтожается  в деструкторе класса, который вызывается когда программу закрывают, примерно так:
....
terminated := true;
PulseEvent(eventHandle)
WaitForSingleObject(ThreadHandle....)
TerminateThread(ThreadHandle)
CloseHandle(ThreadHandle)
CloseHandle(eventHandle)
.....


 
Leonid Troyanovsky ©   (2006-12-07 14:36) [37]


> Mixal__   (07.12.06 14:07) [36]

> Поток уничтожается  в деструкторе класса, который вызывается
> когда программу закрывают, примерно так:


Т.е., есть подозрения, что он не уничтожится при завершении процесса?

И, вообще, не очень понятно, почему для включения насосов
потребовалась dll.

Остается лишь надеяться, что все эти "плагины" не для автоматизации
опасных производств.

--
Regards, LVT.


 
Сергей М. ©   (2006-12-08 09:04) [38]


> Mixal__   (07.12.06 14:07) [36]



> Контроль - это опрос разных датчиков.
>
> Вроде того: запустили вы программу - у вас в разных окошках
> меняются всякие значения - ну допустим температура, давление,
>  скорость воздуха, уровень воды в баке итп. А нажали кнопку
> - у вас хопа и включился насос итп.


Понятно.
Велосипед, значит, изобретаем)

Почитай что-нть научно-популярное про уже существующие SCADA-системы (например, TraceMode), а так же про OPC, ModBus и иже с ними.


> Поток уничтожается  в деструкторе класса, который вызывается
> когда программу закрывают


Какого класса ?
Где он объявлен и где создан его экземпляр ?
Где вызывается деструктор экземпляра ?

Откуда хост-приложение знает про какой-то существующий в DLL доп.поток ?


> WaitForSingleObject(ThreadHandle....)
> TerminateThread(ThreadHandle)


Ерунда какая-то ..
Зачем тут TerminateThread ?

Если все это делается при финализации dll, то код неработоспособен - вызов ф-ции WaitForSingleObject лишен смысла, ибо она никогда не дождется завершения выполнения поточной функции.


 
Mixal__   (2006-12-11 08:03) [39]


> > Mixal__   (07.12.06 14:07) [36]
>
>
>
> > Контроль - это опрос разных датчиков.
> >
> > Вроде того: запустили вы программу - у вас в разных окошках
>
> > меняются всякие значения - ну допустим температура, давление,
>
> >  скорость воздуха, уровень воды в баке итп. А нажали кнопку
>
> > - у вас хопа и включился насос итп.
>
>
> Понятно.
> Велосипед, значит, изобретаем)
>
> Почитай что-нть научно-популярное про уже существующие SCADA-
> системы (например, TraceMode), а так же про OPC, ModBus
> и иже с ними.


Почему Велосипед? Я же не делаю систему управления каким-либо производственным процессом...


> > Поток уничтожается  в деструкторе класса, который вызывается
>
> > когда программу закрывают
>
>
> Какого класса ?
> Где он объявлен и где создан его экземпляр ?
> Где вызывается деструктор экземпляра ?
>
> Откуда хост-приложение знает про какой-то существующий в
> DLL доп.поток ?


Класс объявлен в длл
MCU_1b = class(TInterfacedObject,......,......)

функции вызываемые из главной программы:

function MCU_1b.DoCmd1(...);
begin
end;

function MCU_1b.DoCmd2(...);
begin
end;

......

destructor MCU_1b.destroy;
begin
!
end;


> > WaitForSingleObject(ThreadHandle....)
> > TerminateThread(ThreadHandle)
>
>
> Ерунда какая-то ..
> Зачем тут TerminateThread ?
>
> Если все это делается при финализации dll, то код неработоспособен
> - вызов ф-ции WaitForSingleObject лишен смысла, ибо она
> никогда не дождется завершения выполнения поточной функции.
>


В деструкторе класса (не при финализации длл)  terminated:=true; жду завершения потока некоторое время, если не дожидаюсь - то убиваю поток.


 
Сергей М. ©   (2006-12-11 08:47) [40]


> В деструкторе класса (не при финализации длл)


Если не при финализации этот деструктор вызывается, то когда ?


 
Mixal__   (2006-12-11 09:15) [41]

При финализации длл этот деструктор выполняется... но почему код неработоспособен??


 
Сергей М. ©   (2006-12-11 09:36) [42]

Ничего не понимаю ...

Сначала ты говоришь, что


> В деструкторе класса (не при финализации длл)


А теперь


> При финализации длл этот деструктор выполняется


Вражескую разведку что ли запутываешь ?)


> почему код неработоспособен?


Потому что при финализации dll ты никогда не дождешься сигнала завершения потока, созданного в контексте этой dll.
Следы ведут к блокировке PEB, выполняемой системой всякий раз при загрузке/выгрузке модуля.


 
Mixal__   (2006-12-11 10:44) [43]

Что подразумевалось под финализацией длл?
Я не в курсе как работает программа, подключающая длл. Деструктор вызывается когда пользователь закрывает программу.
Эксперимент показал, что WaitForSingleObject не вернуло WAIT_TIMEOUT...


 
Сергей М. ©   (2006-12-11 10:53) [44]


> Что подразумевалось под финализацией длл?


Код в составе образа dll, вызываемый системой при выгрузке этой dll из АП процесса ее использующего.
Конкретно - вызов функции DllEntryPoint() с параметром PROCESS_DETACH.


> При инициализации dll создаётся поток

Соответственно контрвопрос - что здесь подразумевалось под инициализацией ?


> Деструктор вызывается когда пользователь закрывает программу


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


 
Сергей М. ©   (2006-12-11 10:55) [45]


> Эксперимент показал, что WaitForSingleObject не вернуло
> WAIT_TIMEOUT


Значит эта Wait-функция была вызвана заведомо не при финализации, а раньше.



Страницы: 1 2 вся ветка

Форум: "WinAPI";
Текущий архив: 2007.05.06;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.6 MB
Время: 0.048 c
15-1176208418
Ega23
2007-04-10 16:33
2007.05.06
Знатокам Sybase Power Desigher


1-1173442190
Kolan
2007-03-09 15:09
2007.05.06
Большой шрифт портит форму. Что делать?


15-1176046522
Kerk
2007-04-08 19:35
2007.05.06
Прудников


2-1176732810
mahab
2007-04-16 18:13
2007.05.06
Timage


1-1173684713
zap8
2007-03-12 10:31
2007.05.06
Погрешность в 1 секунду





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский