На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела Visual C++ / MFC / WTL (далее Раздела)
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.

Полезные ссылки:
user posted image FAQ Раздела user posted image Обновления для FAQ Раздела user posted image Поиск по Разделу user posted image MSDN Library Online
Модераторы: ElcnU
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Оптимизация асинхронного I/O в COM порт , многопоточность
    Доброго времени суток, уважаемые!
    ExpandedWrap disabled
      Платформа WIN32, ОС ХР, комилятор BCB 6.xx

    Собственно в чем вопрос - проблема в том, что в потоке в цикле висит алгоритм который проверяет WaitCommEvent(), ERROR_IO_PENDING, наличие данных в очереди - ComStat.cbInQue ну и так далее, и все такое. Все замечательно, все работает, но очень уж беспокоит то, что загрузка ЦП очень высока. Пока обрабатывается один порт одним потоком, все вроде бы и ничего, но в реальности будет обрабатываться много (примерно 30~50) портов, я так полагю, ситуация значительно ухудшится. :(
    Этому есть разумное решение программным способом? ;)
      вот пример из моей проги, может будет полезен. Когда данных для чтения нет, то поток чтения просто ждет когда появятся данные, а не проверяет постоянно очередь.
      инициализация ком порта
      ExpandedWrap disabled
            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 ) )
                                {

      функция чтение крутиться в цикле в отдельном потоке, вот кусок.
      ExpandedWrap disabled
        // 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 будет прочитанные символы
      что непонятно спрашивайте.
        Спасибо, буду пока разбираться, помоему, то что нужно! :yes:
          Используй ReadFile в отдельных потоках, проверено, работает надежно. Дело в том, что приведенный выше пример немного медленней работает и при высокой скорости работы порта может пропускать байты, вернее буфер порта переполняется и байты теряются.
            sploid, я бы чуток упростил программу, учитывая, что ожидание завершения OVERLAPPED-операции может делать сам GetOverlappedResult с флагом bWait = TRUE.
              флаг bWait выставляется в фальс, когда надо завершить поток чтения, т.е. когда случится событие pSiem->m_hEventEndThread.
                sploid, это у тебя для ожидания WaitCommEvent сделано, а для чтения нет, тем более, что можно завершение потока по-другому реализовать, например вручную сигнализировать ивент или просто прибить тред.
                  Спасибо всем еще раз, возник так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();
                  }
                    Используй вместо своей конструкции
                    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 вроде только синхронный.
                      Цитата Паровоз @
                      Асинхронно порт работает только в 98windows, NT/XP вроде только синхронный.
                      Чушь

                      Добавлено
                      Цитата Coffee @
                      Посылаешь ему команду (любую) - во входном буфере ловишь ее имя и все. Никакого тебе ответа от оборудования. Когда делал асинхронно - все было иначе.

                      Странная и непонятная фраза: что значит "ловить имя команды"? Какой ответ от какого оборудования?
                        Цитата Adil @
                        Чушь

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

                        Пришлось переходить к псевдо-синхронной схеме, т.е. использовать асинхронные вызовы ReadFile/WriteFile, а потом ожидать сигнализации события о завершении операции.

                        Добавлено
                        Coffee, при таком количестве портов, возможно, имеет смысл не плодить кучу тредов, а создать пул тредов, каждый из которых будет обрабатывать несколько портов. В таком случае, после запуска операции асинхронно, тебе надо будет, например, с помощью GetOverlappedResult пробегаться по списку ивентов, связанных с операциями по каждому порту. Теоритически, можно рискнуть это сделать в одном треде (но у меня есть ощущение, что не будет успевать).

                        В UNIXах c этим проще - select умеет работать не только с сокетами, но и с дескрипторами обычных файлов и устройств, а в Windows данная возможность не реализована.
                          Цитата 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/ХР синхронная запись/чтение - в моем случае не покатят? Непонимаю... :wall: в мсдн вроде написано мол и то можно и то, только примеры лежат под OVERLAPPED вариант...
                            Coffee, в MSDN написано, что синхронно читать/писать можно, но реалии таковы, то это дело работает только на Win95-98, а на линейке NT5 (2k, XP, 2003) не пашет, про NT4 и ранее не в курсе - не пробовал, так что про синхронную работу - забудь. У меня правда получается работать синхронно с WaitCommEvent, при том, что дескриптор порта открыт с флагом OVERLAPPED, но в доке написано, что такой режим не рекомендуется.

                            Добавлено
                            Собственно вот тема, где обсуждается блокировка при синхронном в/в: [COM-порт]синхронная запись/мониторинг
                              Слушай, а может тебе проверить провода на счет замыкания 2и3 пина. Если они замкнуты, то и ответы будут такими странными. И еще вариант, то, что оборудование такое.
                                Цитата Паровоз @
                                Слушай, а может тебе проверить провода на счет замыкания 2и3 пина. Если они замкнуты, то и ответы будут такими странными. И еще вариант, то, что оборудование такое.

                                2 exodus все понятно, OVERLAPPED forever :)
                                2 Паровоз нет, там стоит PCI модем на котором я и извращаюсь, когда получится с ним перейду на другое оборудование. Да и не может быть так, что одни и те же команды работают асинхронно и не работают синхронно. ИМХО - либо кривая прога - либо виндовый механизм синхронного I/O в ХР...
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0507 ]   [ 16 queries used ]   [ Generated: 20.11.25, 05:42 GMT ]