Оптимизация асинхронного I/O в COM порт
, многопоточность
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.219] |
|
|
Правила раздела Visual C++ / MFC / WTL (далее Раздела)
FAQ Раздела
Обновления для FAQ Раздела
Поиск по Разделу
MSDN Library Online| Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Оптимизация асинхронного I/O в COM порт
, многопоточность
|
Сообщ.
#1
,
|
|
|
|
Доброго времени суток, уважаемые!
![]() ![]() Платформа WIN32, ОС ХР, комилятор BCB 6.xx Собственно в чем вопрос - проблема в том, что в потоке в цикле висит алгоритм который проверяет WaitCommEvent(), ERROR_IO_PENDING, наличие данных в очереди - ComStat.cbInQue ну и так далее, и все такое. Все замечательно, все работает, но очень уж беспокоит то, что загрузка ЦП очень высока. Пока обрабатывается один порт одним потоком, все вроде бы и ничего, но в реальности будет обрабатываться много (примерно 30~50) портов, я так полагю, ситуация значительно ухудшится. Этому есть разумное решение программным способом? |
|
Сообщ.
#2
,
|
|
|
|
вот пример из моей проги, может будет полезен. Когда данных для чтения нет, то поток чтения просто ждет когда появятся данные, а не проверяет постоянно очередь.
инициализация ком порта ![]() ![]() char strNamePort[ 150 ] = { 0 }; sprintf( strNamePort, "COM%d:", nComPort ); m_hComPort = CreateFile( strNamePort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL ); ........... if ( SetCommMask( m_hComPort, EV_RXCHAR ) ) { COMMTIMEOUTS TimeOuts; if ( GetCommTimeouts( m_hComPort, &TimeOuts ) ) { TimeOuts.ReadIntervalTimeout = 40; TimeOuts.ReadTotalTimeoutMultiplier = 0; TimeOuts.ReadTotalTimeoutConstant = 0; TimeOuts.WriteTotalTimeoutMultiplier = 0; TimeOuts.WriteTotalTimeoutConstant = 0; if ( SetCommTimeouts( m_hComPort, &TimeOuts ) ) { функция чтение крутиться в цикле в отдельном потоке, вот кусок. ![]() ![]() // pSiem->m_hComPort - хэндл ком порта // pSiem->m_hEventEndThread - событие что надо закончить поток чтения. DWORD nBytesRead = 0; bool bFlag = true; while ( bFlag ) { // если тут, значит все ок и данные записали, читаем ответ DWORD dwCommEvent; DWORD dwOvRes = 0; // ивент события ком порта OVERLAPPED osStatus = { 0 }; osStatus.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( osStatus.hEvent == NULL ) { TRACE( "ERROR: ивет для события ком порта не создался!\n" ); return; } if ( !WaitCommEvent( pSiem->m_hComPort, &dwCommEvent, &osStatus ) ) { if ( GetLastError( ) == ERROR_IO_PENDING ) { // все ок, ждем события } else { CloseHandle( osStatus.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: WaitCommEvent failed, error = %d!\n", GetLastError( ) ); TRACE( message ); return; } } else { // плохо, не понятная ошибка CloseHandle( osStatus.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: WaitCommEvent failed, error = %d!\n", GetLastError( ) ); TRACE( message ); return; } // ожидаем события не чтение или закрываем поток на чтение HANDLE hEv[ 2 ]; hEv[ 0 ] = pSiem->m_hEventEndThread; hEv[ 1 ] = osStatus.hEvent; DWORD dwRes = WaitForMultipleObjects( 2, hEv, FALSE, INFINITE ); switch( dwRes ) { case WAIT_OBJECT_0: bFlag = false; // завершаем поток break; case WAIT_OBJECT_0 + 1: if ( !GetOverlappedResult( pSiem->m_hComPort, &osStatus, &dwOvRes, FALSE ) ) { // плохо, не понятная ошибка CloseHandle( osStatus.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: GetOverlappedResult failed, error = %d!\n", GetLastError( ) ); TRACE( message ); return; } else { CloseHandle( osStatus.hEvent ); // ивент чтения OVERLAPPED osRead = { 0 }; osRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( osRead.hEvent == NULL ) { TRACE( "ERROR: инет для чтения не создался!\n" ); return; } COMSTAT Comstatus; DWORD dwError; ClearCommError( pSiem->m_hComPort, &dwError, &Comstatus ); char * buffer = new char [ Comstatus.cbInQue + 1 ]; memset( buffer, 0, Comstatus.cbInQue + 1 ); if ( !ReadFile( pSiem->m_hComPort, buffer, Comstatus.cbInQue, &nBytesRead, &osRead ) ) { DWORD dwLastError = GetLastError( ); if( dwLastError != ERROR_IO_PENDING ) { // ошибка чтения CloseHandle( osRead.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: ReadFile failed, error = %d!\n", dwLastError ); TRACE( message ); return; } else { DWORD fRes = WaitForSingleObject( osRead.hEvent, INFINITE ); switch ( fRes ) { case WAIT_OBJECT_0: if ( !GetOverlappedResult( pSiem->m_hComPort, &osRead, &nBytesRead, FALSE)) { // ошибка чтения CloseHandle( osRead.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: GetOverlappedResult failed, error = %d!\n", GetLastError( ) ); TRACE( message ); return; } else { // All ok. } break; default: { // ошибка чтения CloseHandle( osRead.hEvent ); char message[ 200 ] = { 0 }; sprintf( message, "ERROR: Хрен знает что за ошибка, error = %d!\n", GetLastError( ) ); TRACE( message ); return; } }; } } else { // All ok. } в конце по идее в переменной buffer будет прочитанные символы что непонятно спрашивайте. |
|
Сообщ.
#3
,
|
|
|
|
Спасибо, буду пока разбираться, помоему, то что нужно!
|
|
Сообщ.
#4
,
|
|
|
|
Используй ReadFile в отдельных потоках, проверено, работает надежно. Дело в том, что приведенный выше пример немного медленней работает и при высокой скорости работы порта может пропускать байты, вернее буфер порта переполняется и байты теряются.
|
|
Сообщ.
#5
,
|
|
|
|
sploid, я бы чуток упростил программу, учитывая, что ожидание завершения OVERLAPPED-операции может делать сам GetOverlappedResult с флагом bWait = TRUE.
|
|
Сообщ.
#6
,
|
|
|
|
флаг bWait выставляется в фальс, когда надо завершить поток чтения, т.е. когда случится событие pSiem->m_hEventEndThread.
|
|
Сообщ.
#7
,
|
|
|
|
sploid, это у тебя для ожидания WaitCommEvent сделано, а для чтения нет, тем более, что можно завершение потока по-другому реализовать, например вручную сигнализировать ивент или просто прибить тред.
|
|
Сообщ.
#8
,
|
|
|
|
Спасибо всем еще раз, возник такoй вопрос:
Обрисую ситуацию - порядка 30-50 портов, на каждый порт свой поток, пока я данные в порт не отправил и не получил ответ от устройства - делать потоку собственно нечего т.е. дальнейшее выполнение целиком зависит от результатов предыдущих шагов(как я понимаю основная фишка асинхронного метода в продолжении работы потока не заботясь о завершении записи/считывания информации). В моем случае есть ли какой-либо смысл использовать асинхронный I/O? Или проще(и проще ли?) использовать в каждом потоке синхронный метод? И еще одно Порылся поиском по сайту, накопал много доков по портам, в одном из них нашел пример, но вот чего я понять не могу(я все-таки пока далеко не эксперт по портам ) - так это почему он так странно работает? Посылаешь ему команду (любую) - во входном буфере ловишь ее имя и все. Никакого тебе ответа от оборудования. Когда делал асинхронно - все было иначе. Вот пример...Цитата #include <windows.h> #include <iostream.h> #include <conio.h> void main(void) { DCB dcb; COMMTIMEOUTS ct; HANDLE hPort; DWORD bc; DWORD mask; char *buf_out="ATI3"; char *buf_in; dcb.DCBlength=sizeof(DCB); BuildCommDCB("baud=9600 parity=N data=8 stop=1",&dcb); dcb.fNull=TRUE; ct.ReadIntervalTimeout=10; ct.ReadTotalTimeoutMultiplier=ct.ReadTotalTimeoutConstant=0; ct.WriteTotalTimeoutMultiplier=ct.WriteTotalTimeoutConstant=0; hPort=CreateFile("COM3",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); if(hPort==INVALID_HANDLE_VALUE) { MessageBox(NULL,"Невозможно открыть последовательный порт","Error",MB_OK); ExitProcess(1); } SetCommState(hPort,&dcb); SetCommTimeouts(hPort,&ct); PurgeComm(hPort,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); SetupComm(hPort,256,256); SetCommMask(hPort,EV_RXCHAR); buf_in=(char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,strlen(buf_out)+1); cout<<"\nWRITTE\t"<<buf_out; WriteFile(hPort,buf_out,strlen(buf_out),&bc,NULL); WaitCommEvent(hPort,&mask,NULL); ReadFile(hPort,buf_in,strlen(buf_out),&bc,NULL); cout<<"\nREAD\t"<<buf_in; HeapFree(GetProcessHeap(),0,buf_in); CloseHandle(hPort); getch(); } |
|
Сообщ.
#9
,
|
|
|
|
Используй вместо своей конструкции
WaitCommEvent(hPort,&mask,NULL); ReadFile(hPort,buf_in,strlen(buf_out),&bc,NULL); Вот эту if( ReadFile(hPort, buf_in, strlen(buf_out), &bc, 0)){ if (bc > 0){/*что то делаем*/} } ReadFile не вернет управления, пока не заполнит буфер либо не возникнет сбой/таймаут. Совсем необязательно, что буфер будет заполнен, нужно проверять, что bc>0. Асинхронно порт работает только в 98windows, NT/XP вроде только синхронный. |
|
Сообщ.
#10
,
|
|
|
|
Цитата Паровоз @ Чушь Асинхронно порт работает только в 98windows, NT/XP вроде только синхронный. Добавлено Цитата Coffee @ Посылаешь ему команду (любую) - во входном буфере ловишь ее имя и все. Никакого тебе ответа от оборудования. Когда делал асинхронно - все было иначе. Странная и непонятная фраза: что значит "ловить имя команды"? Какой ответ от какого оборудования? |
|
Сообщ.
#11
,
|
|
|
|
Цитата Adil @ Чушь К сожалению, не чушь - сам недавно напоролся на эту фигню. Недавно эту проблему уже обсуждали на форуме. При попытке записать синхронно в порт, тред, который пишет, - блокируется, пока не разблокируется тред, в котором данные из порта синхронно читаются. Т.е. пока используется либо только синхронное чтение, либо только синхронная запись - всё ОК, совместно они друг друга блочат. Пришлось переходить к псевдо-синхронной схеме, т.е. использовать асинхронные вызовы ReadFile/WriteFile, а потом ожидать сигнализации события о завершении операции. Добавлено Coffee, при таком количестве портов, возможно, имеет смысл не плодить кучу тредов, а создать пул тредов, каждый из которых будет обрабатывать несколько портов. В таком случае, после запуска операции асинхронно, тебе надо будет, например, с помощью GetOverlappedResult пробегаться по списку ивентов, связанных с операциями по каждому порту. Теоритически, можно рискнуть это сделать в одном треде (но у меня есть ощущение, что не будет успевать). В UNIXах c этим проще - select умеет работать не только с сокетами, но и с дескрипторами обычных файлов и устройств, а в Windows данная возможность не реализована. |
|
Сообщ.
#12
,
|
|
|
|
Цитата Adil @ Цитата Паровоз @ Чушь Асинхронно порт работает только в 98windows, NT/XP вроде только синхронный. Добавлено Цитата Coffee @ Посылаешь ему команду (любую) - во входном буфере ловишь ее имя и все. Никакого тебе ответа от оборудования. Когда делал асинхронно - все было иначе. Странная и непонятная фраза: что значит "ловить имя команды"? Какой ответ от какого оборудования? 2 Паровоз "ReadFile не вернет управления, пока не заполнит буфер либо не возникнет сбой/таймаут." - верно, он буфер пока не заполнит, он не вернет, но он чем-то не тем его заполняет... и BC>0 тут не катит... чтобы понять что я имею ввиду см. мой ответ Adil'у 2 Adil Поясню что это значит. 1. Асинхронный вариант Посылаю команду ATZ получаю ответ от оборудования - что-то типа "ATZ\r\n\r\nOK\r\n" во входном буфере. Посылаю команду ATH1 получаю ответ от оборудования - что-то типа "\r\nOK\r\n" во входном буфере и характерный звук модема. 1. Синхронный вариант (исходник см.выше) Посылаю команду ATZ получаю во входном буфере "ATZ". Посылаю команду ATH1 получаю во входном буфере "ATH1" и никакого звука от модема. 2 exodus В одном потоке помоему точно не стоит ![]() Так тот исходник что я кинул будет работать под ХР? или под 2000/ХР синхронная запись/чтение - в моем случае не покатят? Непонимаю... в мсдн вроде написано мол и то можно и то, только примеры лежат под OVERLAPPED вариант... |
|
Сообщ.
#13
,
|
|
|
|
Coffee, в MSDN написано, что синхронно читать/писать можно, но реалии таковы, то это дело работает только на Win95-98, а на линейке NT5 (2k, XP, 2003) не пашет, про NT4 и ранее не в курсе - не пробовал, так что про синхронную работу - забудь. У меня правда получается работать синхронно с WaitCommEvent, при том, что дескриптор порта открыт с флагом OVERLAPPED, но в доке написано, что такой режим не рекомендуется.
Добавлено Собственно вот тема, где обсуждается блокировка при синхронном в/в: [COM-порт]синхронная запись/мониторинг |
|
Сообщ.
#14
,
|
|
|
|
Слушай, а может тебе проверить провода на счет замыкания 2и3 пина. Если они замкнуты, то и ответы будут такими странными. И еще вариант, то, что оборудование такое.
|
|
Сообщ.
#15
,
|
|
|
|
Цитата Паровоз @ Слушай, а может тебе проверить провода на счет замыкания 2и3 пина. Если они замкнуты, то и ответы будут такими странными. И еще вариант, то, что оборудование такое. 2 exodus все понятно, OVERLAPPED forever ![]() 2 Паровоз нет, там стоит PCI модем на котором я и извращаюсь, когда получится с ним перейду на другое оборудование. Да и не может быть так, что одни и те же команды работают асинхронно и не работают синхронно. ИМХО - либо кривая прога - либо виндовый механизм синхронного I/O в ХР... |