`
dwtf55dwtf
  • 浏览: 12794 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

深入浅出Win32多线程程序设计之(五):综合实例

 
阅读更多

深入浅出Win32多线程程序设计之(五):综合实例
2010年09月28日
  本章我们将以工业控制和嵌入式系统中运用极为广泛的串口通信为例讲述多线程的典型应用。
  而网络通信也是多线程应用最广泛的领域之一,所以本章的最后一节也将对多线程网络通信进行简短的描述。
  1.串口通信
  在工业控制系统中,工控机(一般都基于PC Windows平台)经常需要与单片机通过串口进行通信。因此,操作和使用PC的串口成为大多数单片机、嵌入式系统领域工程师必须具备的能力。
  串口的使用需要通过三个步骤来完成的:
  (1) 打开通信端口;
  (2) 初始化串口,设置波特率、数据位、停止位、奇偶校验等参数。为了给读者一个直观的印象,下图从Windows的"控制面板->系统->设备管理器->通信端口(COM1)"打开COM的设置窗口:
  
  (3) 读写串口。
  在WIN32平台下,对通信端口进行操作跟基本的文件操作一样。
  创建/打开COM资源
  下列函数如果调用成功,则返回一个标识通信端口的句柄,否则返回-1:
  HADLE CreateFile(PCTSTR lpFileName, //通信端口名,如"COM1"
  WORD dwDesiredAccess, //对资源的访问类型
  WORD dwShareMode, //指定共享模式,COM不能共享,该参数为0
  PSECURITY_ATTRIBUTES lpSecurityAttributes,
  //安全描述符指针,可为NULL
  WORD dwCreationDisposition, //创建方式
  WORD dwFlagsAndAttributes, //文件属性,可为NULL
  HANDLE hTemplateFile //模板文件句柄,置为NULL
  ); 
  获得/设置COM属性
  下列函数可以获得COM口的设备控制块,从而获得相关参数:
  如果要调整通信端口的参数,则需要重新配置设备控制块,再用WIN32 API SetCommState()函数进行设置:
  DCB结构包含了串口的各项参数设置,如下: 
  typedef struct _DCB
  {
  // dcb
  DWORD DCBlength; // sizeof(DCB)
  DWORD BaudRate; // current baud rate
  DWORD fBinary: 1; // binary mode, no EOF check
  DWORD fParity: 1; // enable parity checking
  DWORD fOutxCtsFlow: 1; // CTS output flow control
  DWORD fOutxDsrFlow: 1; // DSR output flow control
  DWORD fDtrControl: 2; // DTR flow control type
  DWORD fDsrSensitivity: 1; // DSR sensitivity
  DWORD fTXContinueOnXoff: 1; // XOFF continues Tx
  DWORD fOutX: 1; // XON/XOFF out flow control
  DWORD fInX: 1; // XON/XOFF in flow control
  DWORD fErrorChar: 1; // enable error replacement
  DWORD fNull: 1; // enable null stripping
  DWORD fRtsControl: 2; // RTS flow control
  DWORD fAbortOnError: 1; // abort reads/writes on error
  DWORD fDummy2: 17; // reserved
  WORD wReserved; // not currently used
  WORD XonLim; // transmit XON threshold
  WORD XoffLim; // transmit XOFF threshold
  BYTE ByteSize; // number of bits/byte, 4-8
  BYTE Parity; // 0-4=no,odd,even,mark,space
  BYTE StopBits; // 0,1,2 = 1, 1.5, 2
  char XonChar; // Tx and Rx XON character
  char XoffChar; // Tx and Rx XOFF character
  char ErrorChar; // error replacement character
  char EofChar; // end of input character
  char EvtChar; // received event character
  WORD wReserved1; // reserved; do not use
  } DCB; 
  读写串口
  在读写串口之前,还要用PurgeComm()函数清空缓冲区,并用SetCommMask ()函数设置事件掩模来监视指定通信端口上的事件,其原型为:
  串口上可能发生的事件如下表所示:
  值 事件描述 
  EV_BREAK A break was detected on input. 
  EV_CTS The CTS (clear-to-send) signal changed state. 
  EV_DSR The DSR(data-set-ready) signal changed state. 
  EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. 
  EV_RING A ring indicator was detected. 
  EV_RLSD The RLSD (receive-line-signal-detect) signal changed state. 
  EV_RXCHAR A character was received and placed in the input buffer. 
  EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function. 
  EV_TXEMPTY The last character in the output buffer was sent. 
  在设置好事件掩模后,我们就可以利用WaitCommEvent()函数来等待串口上发生事件,其函数原型为:
  我们可以在发生事件后,根据相应的事件类型,进行串口的读写操作:
  BOOL ReadFile(HANDLE hFile, //标识通信端口的句柄
  LPVOID lpBuffer, //输入数据Buffer指针
  DWORD nNumberOfBytesToRead, // 需要读取的字节数
  LPDWORD lpNumberOfBytesRead, //实际读取的字节数指针
  LPOVERLAPPED lpOverlapped //指向overlapped结构
  );
  BOOL WriteFile(HANDLE hFile, //标识通信端口的句柄
  LPCVOID lpBuffer, //输出数据Buffer指针
  DWORD nNumberOfBytesToWrite, //需要写的字节数
  LPDWORD lpNumberOfBytesWritten, //实际写入的字节数指针
  LPOVERLAPPED lpOverlapped //指向overlapped结构
  ); 
  2.工程实例
  下面我们用第1节所述API实现一个多线程的串口通信程序。这个例子工程(工程名为MultiThreadCom)的界面很简单,如下图所示:
  
  它是一个多线程的应用程序,包括两个工作者线程,分别处理串口1和串口2。为了简化问题,我们让连接两个串口的电缆只包含RX、TX两根连线(即不以硬件控制RS-232,串口上只会发生EV_TXEMPTY、EV_RXCHAR事件)。
  在工程实例的BOOL CMultiThreadComApp::InitInstance()函数中,启动并设置COM1和COM2,其源代码为:
  BOOL CMultiThreadComApp::InitInstance()
  {
  AfxEnableControlContainer();
  //打开并设置COM1
  hComm1=CreateFile("COM1", GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL);
  if (hComm1==(HANDLE)-1)
  {
  AfxMessageBox("打开COM1失败");
  return false;
  }
  else
  {
  DCB wdcb;
  GetCommState (hComm1,&wdcb);
  wdcb.BaudRate=9600;
  SetCommState (hComm1,&wdcb);
  PurgeComm(hComm1,PURGE_TXCLEAR);
  }
  //打开并设置COM2
  hComm2=CreateFile("COM2", GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL);
  if (hComm2==(HANDLE)-1)
  {
  AfxMessageBox("打开COM2失败");
  return false;
  }
  else
  {
  DCB wdcb; 
  GetCommState (hComm2,&wdcb);
  wdcb.BaudRate=9600;
  SetCommState (hComm2,&wdcb);
  PurgeComm(hComm2,PURGE_TXCLEAR);
  }
  CMultiThreadComDlg dlg;
  m_pMainWnd = &dlg;
  int nResponse = dlg.DoModal();
  if (nResponse == IDOK)
  {
  // TODO: Place code here to handle when the dialog is
  // dismissed with OK
  }
  else if (nResponse == IDCANCEL)
  {
  // TODO: Place code here to handle when the dialog is
  // dismissed with Cancel
  }
  return FALSE;
  } 
  此后我们在对话框CMultiThreadComDlg的初始化函数OnInitDialog中启动两个分别处理COM1和COM2的线程:
  BOOL CMultiThreadComDlg::OnInitDialog()
  {
  CDialog::OnInitDialog(); 
  // Add "About..." menu item to system menu.
  // IDM_ABOUTBOX must be in the system command range.
  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX AppendMenu(MF_SEPARATOR);
  pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
  }
  // Set the icon for this dialog. The framework does this automatically
  // when the application's main window is not a dialog
  SetIcon(m_hIcon, TRUE); // Set big icon
  SetIcon(m_hIcon, FALSE); // Set small icon
  // TODO: Add extra initialization here
  //启动串口1处理线程 
  DWORD nThreadId1;
  hCommThread1 = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
  (LPTHREAD_START_ROUTINE)Com1ThreadProcess, AfxGetMainWnd()->m_hWnd, 0, &nThreadId1);
  if (hCommThread1 == NULL)
  {
  AfxMessageBox("创建串口1处理线程失败");
  return false;
  }
  //启动串口2处理线程
  DWORD nThreadId2;
  hCommThread2 = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
  (LPTHREAD_START_ROUTINE)Com2ThreadProcess, AfxGetMainWnd()->m_hWnd, 0, &nThreadId2);
  if (hCommThread2 == NULL)
  {
  AfxMessageBox("创建串口2处理线程失败");
  return false;
  }
  return TRUE; // return TRUE unless you set the focus to a control
  } 
  两个串口COM1和COM2对应的线程处理函数等待串口上发生事件,并根据事件类型和自身缓冲区是否有数据要发送进行相应的处理,其源代码为:
  DWORD WINAPI Com1ThreadProcess(HWND hWnd//主窗口句柄)
  {
  DWORD wEven;
  char str[10]; //读入数据
  SetCommMask(hComm1, EV_RXCHAR | EV_TXEMPTY);
  while (TRUE)
  {
  WaitCommEvent(hComm1, &wEven, NULL);
  if(wEven = 0)
  {
  CloseHandle(hCommThread1);
  hCommThread1 = NULL;
  ExitThread(0);
  }
  else
  {
  switch (wEven)
  {
  case EV_TXEMPTY:
  if (wTxPos 内存恢复:
  CSerialPort::CSerialPort()
  {
  m_hComm = NULL;
  // initialize overlapped structure members to zero
  m_ov.Offset = 0;
  m_ov.OffsetHigh = 0;
  // create events
  m_ov.hEvent = NULL;
  m_hWriteEvent = NULL;
  m_hShutdownEvent = NULL;
  m_szWriteBuffer = NULL;
  m_bThreadAlive = FALSE;
  }
  //
  // Delete dynamic memory
  //
  CSerialPort::~CSerialPort()
  {
  do
  {
  SetEvent(m_hShutdownEvent);
  }
  while (m_bThreadAlive);
  TRACE("Thread ended\n");
  delete []m_szWriteBuffer;
  } 
  3.2.2核心函数:初始化串口
  在初始化串口函数中,将打开串口,设置相关参数,并创建串口相关的用户控制事件,初始化临界区(Critical Section),以成队的EnterCriticalSection()、LeaveCriticalSection()函数进行资源的排它性访问:
  BOOL CSerialPort::InitPort(CWnd *pPortOwner,
  // the owner (CWnd) of the port (receives message)
  UINT portnr, // portnumber (1..4)
  UINT baud, // baudrate
  char parity, // parity
  UINT databits, // databits
  UINT stopbits, // stopbits
  DWORD dwCommEvents, // EV_RXCHAR, EV_CTS etc
  UINT writebuffersize) // size to the writebuffer
  {
  assert(portnr > 0 && portnr read/write types
  0, // comm devices must be opened with exclusive access
  NULL, // no security attributes
  OPEN_EXISTING, // comm devices must use OPEN_EXISTING
  FILE_FLAG_OVERLAPPED, // Async I/O
  0); // template must be 0 for comm devices
  if (m_hComm == INVALID_HANDLE_VALUE)
  {
  // port not found
  delete []szPort;
  delete []szBaud;
  return FALSE;
  }
  // set the timeout values
  m_CommTimeouts.ReadIntervalTimeout = 1000;
  m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;
  m_CommTimeouts.ReadTotalTimeoutConstant = 1000;
  m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;
  m_CommTimeouts.WriteTotalTimeoutConstant = 1000;
  // configure
  if (SetCommTimeouts(m_hComm, &m_CommTimeouts))
  {
  if (SetCommMask(m_hComm, dwCommEvents))
  {
  if (GetCommState(m_hComm, &m_dcb))
  {
  m_dcb.fRtsControl = RTS_CONTROL_ENABLE; // set RTS bit high!
  if (BuildCommDCB(szBaud, &m_dcb))
  {
  if (SetCommState(m_hComm, &m_dcb))
  ;
  // normal operation... continue
  else
  ProcessErrorMessage("SetCommState()");
  }
  else
  ProcessErrorMessage("BuildCommDCB()");
  }
  else
  ProcessErrorMessage("GetCommState()");
  }
  else
  ProcessErrorMessage("SetCommMask()");
  }
  else
  ProcessErrorMessage("SetCommTimeouts()");
  delete []szPort;
  delete []szBaud;
  // flush the port
  PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  // release critical section
  LeaveCriticalSection(&m_csCommunicationSync);
  TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);
  return TRUE;
  } 
  3.3.3核心函数:串口线程控制函数
  串口线程处理函数是整个类中最核心的部分,它主要完成两类工作:
  (1)利用WaitCommEvent函数对串口上发生的事件进行获取并根据事件的不同类型进行相应的处理;
  (2)利用WaitForMultipleObjects函数对串口相关的用户控制事件进行等待并做相应处理。
  UINT CSerialPort::CommThread(LPVOID pParam)
  {
  // Cast the void pointer passed to the thread back to
  // a pointer of CSerialPort class
  CSerialPort *port = (CSerialPort*)pParam;
  // Set the status variable in the dialog class to
  // TRUE to indicate the thread is running.
  port->m_bThreadAlive = TRUE;
  // Misc. variables
  DWORD BytesTransfered = 0;
  DWORD Event = 0;
  DWORD CommEvent = 0;
  DWORD dwError = 0;
  COMSTAT comstat;
  BOOL bResult = TRUE;
  // Clear comm buffers at startup
  if (port->m_hComm)
  // check if the port is opened
  PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  // begin forever loop. This loop will run as long as the thread is alive.
  for (;;)
  {
  // Make a call to WaitCommEvent(). This call will return immediatly
  // because our port was created as an async port (FILE_FLAG_OVERLAPPED
  // and an m_OverlappedStructerlapped structure specified). This call will cause the
  // m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to
  // be placed in a non-signeled state if there are no bytes available to be read,
  // or to a signeled state if there are bytes available. If this event handle
  // is set to the non-signeled state, it will be set to signeled when a
  // character arrives at the port.
  // we do this for each port!
  bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);
  if (!bResult)
  {
  // If WaitCommEvent() returns FALSE, process the last error to determin
  // the reason..
  switch (dwError = GetLastError())
  {
  case ERROR_IO_PENDING:
  {
  // This is a normal return value if there are no bytes
  // to read at the port.
  // Do nothing and continue
  break;
  }
  case 87:
  {
  // Under Windows NT, this value is returned for some reason.
  // I have not investigated why, but it is also a valid reply
  // Also do nothing and continue.
  break;
  }
  default:
  {
  // All other error codes indicate a serious error has
  // occured. Process this error.
  port->ProcessErrorMessage("WaitCommEvent()");
  break;
  }
  }
  }
  else
  {
  // If WaitCommEvent() returns TRUE, check to be sure there are
  // actually bytes in the buffer to read.
  //
  // If you are reading more than one byte at a time from the buffer
  // (which this program does not do) you will have the situation occur
  // where the first byte to arrive will cause the WaitForMultipleObjects()
  // function to stop waiting. The WaitForMultipleObjects() function
  // resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state
  // as it returns.
  //
  // If in the time between the reset of this event and the call to
  // ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again
  // to the signeled state. When the call to ReadFile() occurs, it will
  // read all of the bytes from the buffer, and the program will
  // loop back around to WaitCommEvent().
  //
  // At this point you will be in the situation where m_OverlappedStruct.hEvent is set,
  // but there are no bytes available to read. If you proceed and call
  // ReadFile(), it will return immediatly due to the async port setup, but
  // GetOverlappedResults() will not return until the next character arrives.
  //
  // It is not desirable for the GetOverlappedResults() function to be in
  // this state. The thread shutdown event (event 0) and the WriteFile()
  // event (Event2) will not work if the thread is blocked by GetOverlappedResults().
  //
  // The solution to this is to check the buffer with a call to ClearCommError().
  // This call will reset the event handle, and if there are no bytes to read
  // we can loop back through WaitCommEvent() again, then proceed.
  // If there are really bytes to read, do nothing and proceed.
  bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  if (comstat.cbInQue == 0)
  continue;
  } // end if bResult
  // Main wait function. This function will normally block the thread
  // until one of nine events occur that require action.
  Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);
  switch (Event)
  {
  case 0:
  {
  // Shutdown event. This is event zero so it will be
  // the higest priority and be serviced first.
  port->m_bThreadAlive = FALSE;
  // Kill this thread. break is not needed, but makes me feel better.
  AfxEndThread(100);
  break;
  }
  case 1:
  // read event
  {
  GetCommMask(port->m_hComm, &CommEvent);
  if (CommEvent &EV_CTS)
  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
  if (CommEvent &EV_RXFLAG)
  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED,(WPARAM)0, (LPARAM)port->m_nPortNr);
  if (CommEvent &EV_BREAK)
  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED,(WPARAM)0, (LPARAM)port->m_nPortNr);
  if (CommEvent &EV_ERR)
  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM)0, (LPARAM)port->m_nPortNr);
  if (CommEvent &EV_RING)
  ::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED,(WPARAM)0, (LPARAM)port->m_nPortNr);
  if (CommEvent &EV_RXCHAR)
  // Receive character event from port.
  ReceiveChar(port, comstat);
  break;
  }
  case 2:
  // write event
  {
  // Write character event from port
  WriteChar(port);
  break;
  }
  } // end switch
  } // close forever loop
  return 0;
  } 
  下列三个函数用于对串口线程进行启动、挂起和恢复:
  //
  // start comm watching
  //
  BOOL CSerialPort::StartMonitoring()
  {
  if (!(m_Thread = AfxBeginThread(CommThread, this)))
  return FALSE;
  TRACE("Thread started\n");
  return TRUE;
  }
  //
  // Restart the comm thread
  //
  BOOL CSerialPort::RestartMonitoring()
  {
  TRACE("Thread resumed\n");
  m_Thread->ResumeThread();
  return TRUE;
  }
  //
  // Suspend the comm thread
  //
  BOOL CSerialPort::StopMonitoring()
  {
  TRACE("Thread suspended\n");
  m_Thread->SuspendThread();
  return TRUE;
  } 
  3.3.4读写串口
  下面一组函数是用户对串口进行读写操作的接口:
  //
  // Write a character.
  //
  void CSerialPort::WriteChar(CSerialPort *port)
  {
  BOOL bWrite = TRUE;
  BOOL bResult = TRUE;
  DWORD BytesSent = 0;
  ResetEvent(port->m_hWriteEvent);
  // Gain ownership of the critical section
  EnterCriticalSection(&port->m_csCommunicationSync);
  if (bWrite)
  {
  // Initailize variables
  port->m_ov.Offset = 0;
  port->m_ov.OffsetHigh = 0;
  // Clear buffer
  PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
  bResult = WriteFile(port->m_hComm, // Handle to COMM Port
  port->m_szWriteBuffer, // Pointer to message buffer in calling finction
  strlen((char*)port->m_szWriteBuffer), // Length of message to send
  &BytesSent, // Where to store the number of bytes sent
  &port->m_ov); // Overlapped structure
  // deal with any error codes
  if (!bResult)
  {
  DWORD dwError = GetLastError();
  switch (dwError)
  {
  case ERROR_IO_PENDING:
  {
  // continue to GetOverlappedResults()
  BytesSent = 0;
  bWrite = FALSE;
  break;
  }
  default:
  {
  // all other error codes
  port->ProcessErrorMessage("WriteFile()");
  }
  }
  }
  else
  {
  LeaveCriticalSection(&port->m_csCommunicationSync);
  }
  } // end if(bWrite)
  if (!bWrite)
  {
  bWrite = TRUE;
  bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  &port->m_ov, // Overlapped structure
  &BytesSent, // Stores number of bytes sent
  TRUE); // Wait flag
  LeaveCriticalSection(&port->m_csCommunicationSync);
  // deal with the error code
  if (!bResult)
  {
  port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");
  }
  } // end if (!bWrite)
  // Verify that the data size send equals what we tried to send
  if (BytesSent != strlen((char*)port->m_szWriteBuffer))
  {
  TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n",
  BytesSent, strlen((char*)port->m_szWriteBuffer));
  }
  }
  //
  // Character received. Inform the owner
  //
  void CSerialPort::ReceiveChar(CSerialPort *port, COMSTAT comstat)
  {
  BOOL bRead = TRUE;
  BOOL bResult = TRUE;
  DWORD dwError = 0;
  DWORD BytesRead = 0;
  unsigned char RXBuff;
  for (;;)
  {
  // Gain ownership of the comm port critical section.
  // This process guarantees no other part of this program
  // is using the port object.
  EnterCriticalSection(&port->m_csCommunicationSync);
  // ClearCommError() will update the COMSTAT structure and
  // clear any other errors.
  bResult = ClearCommError(port->m_hComm, &dwError, &comstat);
  LeaveCriticalSection(&port->m_csCommunicationSync);
  // start forever loop. I use this type of loop because I
  // do not know at runtime how many loops this will have to
  // run. My solution is to start a forever loop and to
  // break out of it when I have processed all of the
  // data available. Be careful with this approach and
  // be sure your loop will exit.
  // My reasons for this are not as clear in this sample
  // as it is in my production code, but I have found this
  // solutiion to be the most efficient way to do this.
  if (comstat.cbInQue == 0)
  {
  // break out when all bytes have been read
  break;
  }
  EnterCriticalSection(&port->m_csCommunicationSync);
  if (bRead)
  {
  bResult = ReadFile(port->m_hComm, // Handle to COMM port
  &RXBuff, // RX Buffer Pointer
  1, // Read one byte
  &BytesRead, // Stores number of bytes read
  &port->m_ov); // pointer to the m_ov structure
  // deal with the error code
  if (!bResult)
  {
  switch (dwError = GetLastError())
  {
  case ERROR_IO_PENDING:
  {
  // asynchronous i/o is still in progress
  // Proceed on to GetOverlappedResults();
  bRead = FALSE;
  break;
  }
  default:
  {
  // Another error has occured. Process this error.
  port->ProcessErrorMessage("ReadFile()");
  break;
  }
  }
  }
  else
  {
  // ReadFile() returned complete. It is not necessary to call GetOverlappedResults()
  bRead = TRUE;
  }
  } // close if (bRead)
  if (!bRead)
  {
  bRead = TRUE;
  bResult = GetOverlappedResult(port->m_hComm, // Handle to COMM port
  &port->m_ov, // Overlapped structure
  &BytesRead, // Stores number of bytes read
  TRUE); // Wait flag
  // deal with the error code
  if (!bResult)
  {
  port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");
  }
  } // close if (!bRead)
  LeaveCriticalSection(&port->m_csCommunicationSync);
  // notify parent that a byte was received
  ::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM)RXBuff,(LPARAM)port->m_nPortNr);
  } // end forever loop
  }
  //
  // Write a string to the port
  //
  void CSerialPort::WriteToPort(char *string)
  {
  assert(m_hComm != 0);
  memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));
  strcpy(m_szWriteBuffer, string);
  // set event for write
  SetEvent(m_hWriteEvent);
  }
  //
  // Return the output buffer size
  //
  DWORD CSerialPort::GetWriteBufferSize()
  {
  return m_nWriteBufferSize;
  } 
  3.3.5控制接口
  应用程序员使用下列一组public函数可以获取串口的DCB及串口上发生的事件:
  3.3.6错误处理
  //
  // If there is a error, give the right message
  //
  void CSerialPort::ProcessErrorMessage(char *ErrorText)
  {
  char *Temp = new char[200];
  LPVOID lpMsgBuf;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  // Default language
  (LPTSTR) &lpMsgBuf, 0, NULL);
  sprintf(Temp,
  "WARNING: %s Failed with the following error: \n%s\nPort: %d\n", (char*)
  ErrorText, lpMsgBuf, m_nPortNr);
  MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);
  LocalFree(lpMsgBuf);
  delete []Temp;
  } 
  仔细分析Remon Spekreijse的CSerialPort类对我们理解多线程及其同步机制是大有益处的,从 http://codeguru.earthweb.com/network/serialport.sh tml我们可以获取CSerialPort类的 介绍与工程实例。另外,电子工业出版社《Visual C++/Turbo C串口通信编程实践》一书的作者龚建伟也编写了一个使用CSerialPort类的例子,可以从http://www.gjwtech.com /scomm/sc2serialportclass.htm获得详情。
  4.多线程网络通信
  在网络通信中使用多线程主要有两种途径,即主监控线程和线程池。 
  4.1主监控线程
  这种方式指的是程序中使用一个主线程监控某特定端口,一旦在这个端口上发生连接请求,则主监控线程动态使用CreateThread派生出新的子线程处理该请求。主线程在派生子线程后不再对子线程加以控制和调度,而由子线程独自和客户方发生连接并处理异常。
  使用这种方法的优点是:
  (1)可以较快地实现原型设计,尤其在用户数目较少、连接保持时间较长时有表现较好;
  (2)主线程不与子线程发生通信,在一定程度上减少了系统资源的消耗。
  其缺点是:
  (1)生成和终止子线程的开销比较大;
  (2)对远端用户的控制较弱。
  这种多线程方式总的特点是"动态生成,静态调度"。
  4.2线程池
  这种方式指的是主线程在初始化时静态地生成一定数量的悬挂子线程,放置于线程池中。随后,主线程将对这些悬挂子线程进行动态调度。一旦客户发出连接请求,主线程将从线程池中查找一个悬挂的子线程:
  (1)如果找到,主线程将该连接分配给这个被发现的子线程。子线程从主线程处接管该连接,并与用户通信。当连接结束时,该子线程将自动悬挂,并进人线程池等待再次被调度;
  (2)如果当前已没有可用的子线程,主线程将通告发起连接的客户。
  使用这种方法进行设计的优点是:
  (1)主线程可以更好地对派生的子线程进行控制和调度;
  (2)对远程用户的监控和管理能力较强。
  虽然主线程对子线程的调度要消耗一定的资源,但是与主监控线程方式中派生和终止线程所要耗费的资源相比,要少很多。因此,使用该种方法设计和实现的系统在客户端连接和终止变更频繁时有上佳表现。
  这种多线程方式总的特点是"静态生成,动态调度"。 
分享到:
评论

相关推荐

    深入浅出Win32多线程程序设计之综合实例

    想深入学习多线程的一定要看看的哦

    Windows多线程

    《Windows多线程编程技术与实例》-郝文化-源代码 VC多线程编程.CHM Win32多线程.pdf Win32多线程程序设计(完美版).pdf 多线程编程基础.pdf 深入浅出Win32多线程程序设计.pdf

    深入浅出MFC-简体版(2)PDF

    多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态...

    一些C++的资料

    7. 深入浅出Win32多线程程序设计 8. C++面向对象程序设计-谭浩强 9. linux从入门到精通教程pdf完整版 10.Linux开发指南 11.Linux网络编程 12.Qt编程及应用经典教程 13.史上最牛的Linux内核学习方法论

    深入浅出MFC 2e

    多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态...

    侯捷- -深入浅出MFC

    多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态...

    深入浅出MFC【侯捷】

    多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态...

    侯捷 深入浅出MFC

    这本书适合谁 深入浅出MFC是一本介绍MFC(Microsoft ...14章~16 章是第二版新增内容,主题分别是MFC 多线程程序设计、Custom AppWizard、以及如何使用Component Gallery 提供的ActiveX controls 和components。

    深入浅出MFC 第二版

    这本书适合谁 深入淺出 MFC是一本介绍MFC(Microsoft ...14 章~16 章是第二版新增内容,主题分别是MFC 多线程程序设计、Custom AppWizard、 以及如何使用Component Gallery 提供的ActiveX controls 和components。

    vc++ 开发实例源码包

    如题,非常好的界面设计实例,自绘控件的实现。 Mufan MP3播放的实现。 MYICQ 0.8 alpha1 (仿腾讯QQ整套聊天系统) 老版qq系统的实现。 MyIE3.0浏览器源代码 如题。完整的代码,重载控件实现,非常适合初学者。 ...

    《Windows网络编程技术》高清PDF版+随书源码

    本书论述深入浅出、用大量实例详解了微软网络API函数的应用。配套光盘包含了所有实例代码,方便读者使用。本书适合中、高级程序设计人员以及网络设计与管理人员参考。包 装: 平装本书通过应用实例由浅入深引入VC++...

    WINDOWS网络编程技术.pdf

    本书论述深入浅出、用大量实例详解了微软网络API函数的应用。配套光盘包含了所有实例代码,方便读者使用。本书适合中、高级程序设计人员以及网络设计与管理人员参考。包 装: 平装本书通过应用实例由浅入深引入VC++...

    vc++ 应用源码包_1

    vc++动态链接库(dll)编程深入浅出 内含开发文档。主要是对动态链接库的教程。 vc++动态链接库编程之DLL典型实例源代码下载 VC++仿Dreamweaver取色器源代码 VC++挂机锁屏系统源程序 VC++建立桌面或开始菜单快捷方式 ...

    vc++ 应用源码包_5

    vc++动态链接库(dll)编程深入浅出 内含开发文档。主要是对动态链接库的教程。 vc++动态链接库编程之DLL典型实例源代码下载 VC++仿Dreamweaver取色器源代码 VC++挂机锁屏系统源程序 VC++建立桌面或开始菜单快捷方式 ...

    vc++ 应用源码包_2

    vc++动态链接库(dll)编程深入浅出 内含开发文档。主要是对动态链接库的教程。 vc++动态链接库编程之DLL典型实例源代码下载 VC++仿Dreamweaver取色器源代码 VC++挂机锁屏系统源程序 VC++建立桌面或开始菜单快捷方式 ...

    vc++ 应用源码包_3

    vc++动态链接库(dll)编程深入浅出 内含开发文档。主要是对动态链接库的教程。 vc++动态链接库编程之DLL典型实例源代码下载 VC++仿Dreamweaver取色器源代码 VC++挂机锁屏系统源程序 VC++建立桌面或开始菜单快捷方式 ...

    vc++ 应用源码包_6

    vc++动态链接库(dll)编程深入浅出 内含开发文档。主要是对动态链接库的教程。 vc++动态链接库编程之DLL典型实例源代码下载 VC++仿Dreamweaver取色器源代码 VC++挂机锁屏系统源程序 VC++建立桌面或开始菜单快捷方式 ...

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

Global site tag (gtag.js) - Google Analytics