线程同步的四种方式以及串口通信

一、线程的创建

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, 
            DWORD dwStackSize,
            LPTHREAD_START_ROUTINE lpStartAddress,
            LPVOID lpParameter,
            DWORD dwCreationFlags,
            LPDWORD lpThreadId);  
该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:
  lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL;   
  dwStackSize:指定了线程的堆栈深度,一般都设置为0;
lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名;
 lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数;
 dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;
 lpThreadId:该参数返回所创建线程的ID; 如果创建成功则返回线程的句柄,否则返回NULL。
1     //创建写串口线程    
2     m_hWriteThread = CreateThread(NULL,0,MyThread2,0,0,NULL);
3 
4     //创建读串口线程
5     m_hReadThread = CreateThread(NULL,0,MyThread1,0,0,NULL);
二、线程的同步
1.临界区
  临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
所需要的函数:

  CRITICAL_SECTION  //临界区结构,必须要首先声明一临界区结构
  InitializeCriticalSection()//初始化临界区
  LeaveCriticalSection()//离开临界区
  EnterCriticalSection()//进入临界区

 2.事件内核对象

   首先需要创建事件内核对象

1 HANDLECreateEvent(
2 LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全属性
3 BOOLbManualReset,// 复位方式
4 BOOLbInitialState,// 初始状态
5 LPCTSTRlpName // 对象名称
6 );

  然后在线程中引用WaitForSingleObject(hEvent,INFINITE);或WaitForMultipleObjects()

1 WaitForMultipleObjects(
2  DWORD nCount,              // 等待句柄数
3  CONST HANDLE *lpHandles,   // 句柄数组首地址
4  BOOL fWaitAll,             // 等待标志
5  DWORD dwMilliseconds       // 等待时间间隔
6 );

使用过后要释放对象 如:SetEvent(hEvent);//设置事件

 3.信号量内核对象

 信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。

  主要函数:CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、WaitForSingleObject()和WaitForMultipleObjects();

//创建一个信号量内核对象
1
HANDLE CreateSemaphore( 2  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性指针 3  LONG lInitialCount,               // 初始计数 4  LONG lMaximumCount,               // 最大计数 5  LPCTSTR lpName                  // 对象名指针 6 );
//用来根据信号量名打开在其他进程中创建的信号量
1
HANDLE OpenSemaphore( 2  DWORD dwDesiredAccess,   // 访问标志 3  BOOL bInheritHandle,    // 继承标志 4  LPCTSTR lpName        // 信号量名 5 );
//释放资源
1
BOOL ReleaseSemaphore( 2  HANDLE hSemaphore,    // 信号量句柄 3  LONG lReleaseCount,   // 计数递增数量 4  LPLONG lpPreviousCount // 先前计数 5 );

  4.互斥内核对象

   互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。

 函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects();

//创建一互斥对象
1
HANDLE CreateMutex( 2   LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性指针 3   BOOL bInitialOwner, // 初始拥有者 4   LPCTSTR lpName // 互斥对象名 5 );
//打开一互斥对象
1
HANDLE OpenMutex( 2   DWORD dwDesiredAccess, // 访问标志 3   BOOL bInheritHandle, // 继承标志 4   LPCTSTR lpName // 互斥对象名 5 );
1  BOOL ReleaseMutex(HANDLE hMutex);//释放资源

三、串口通信

  串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。

  串口通信最重要的参数是波特率数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

  基本过程:打开串口,配置串口,发送数据或接收数据,关闭串口。

  1.打开串口使用CreatFile()函数

1 HANDLE CreateFile(
2 LPCTSTR lpFileName,    //指向文件名的指针
3 DWORD dwDesiredAccess, //访问模式(写/读)
4 DWORD dwShareMode,     //共享模式,串口不能共享,默认为0
5 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
6 DWORD dwCreationDisposition, //如何创建
7 DWORD dwFlagsAndAttributes, //文件属性
8 HANDLE hTemplateFile     //用于复制文件句柄
9 );

2.配置串口

  主要是配置波特率、数据位、停止位和奇偶校验等,还有超时时间。

3.读写串口

//写串口
1
BOOL WriteFile( 2 HANDLE hFile, // 文件句柄 3 LPCVOID lpBuffer, // 数据缓存区指针 4 DWORD nNumberOfBytesToWrite, // 你要写的字节数 5 LPDWORD lpNumberOfBytesWritten, // 用于保存实际写入字节数的存储区域的指针 6 LPOVERLAPPED lpOverlapped // OVERLAPPED结构体指针 7 );
//读串口
1
BOOL ReadFile( 2 HANDLE hFile, //文件的句柄 3 LPVOID lpBuffer, //用于保存读入数据的一个缓冲区 4 DWORD nNumberOfBytesToRead, //要读入的字节数 5 LPDWORD lpNumberOfBytesRead, //指向实际读取字节数的指针 6 LPOVERLAPPED lpOverlapped //如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参数引用一个特殊的结构。该结构定义了一次异步读取操作。否则,应将这个参数设为NULL 7 );

4.关闭串口

头文件记得包含

#include "tchar.h"

// com_thread.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include "iostream"
using namespace std;

//全局变量
HANDLE hMutex = NULL;//创建互斥信号量
HANDLE hComm = NULL;//定义串口句柄
OVERLAPPED m_overlapped;//Overlapped结构,重叠I/O
COMSTAT comstat;//串口传输数据的方式
DWORD m_dwCommEvents;//串口事件
HANDLE hSemaphore = NULL;// 信号量对象句柄
CRITICAL_SECTION cti;//临界区对象,在不保证线程顺序的情况下同步
HANDLE hEvent = NULL;//事件内核对象,可通过通知操作实现线程同步
HANDLE m_hReadThread  = NULL;
HANDLE m_hWriteThread = NULL;

bool openport(TCHAR *portname)
{
    hComm = CreateFile(    portname,                        //串口号
        GENERIC_READ| GENERIC_WRITE,    //允许读写
        0,                                //允许独占
        0,                                //安全属性默认
        OPEN_EXISTING,                    //只打开不创建
        FILE_FLAG_OVERLAPPED,            //异步I/O
        NULL);                            //通讯设备不使用模板
    if (hComm == INVALID_HANDLE_VALUE)
    {
        CloseHandle(hComm);
        return FALSE;
    }
    else 
        return TRUE;
}

bool setDCB(int rate_arg)
{
    DCB dcb;
    int rate = rate_arg;
    memset(&dcb,0,sizeof(dcb));
    if (!GetCommState(hComm,&dcb))
    {
        return FALSE;
    }

    //set DCB 
    dcb.DCBlength = sizeof(dcb);    //dcb长度
    dcb.BaudRate = rate;            //波特率
    dcb.ByteSize = 8;                //数据位采用八位
    dcb.Parity = NOPARITY;            //不采用奇偶校验
    dcb.fParity = 0;                //
    dcb.StopBits = ONESTOPBIT;                //采用一位停止位
    dcb.fOutxCtsFlow    = 0;         
    dcb.fOutxDsrFlow    = 0;         
    dcb.fDtrControl     = DTR_CONTROL_DISABLE;       
    dcb.fDsrSensitivity = 0;         
    dcb.fRtsControl     = RTS_CONTROL_DISABLE;     
    dcb.fOutX           = 0;        
    dcb.fInX            = 0;

    /*--------------------------------------------------------------------*/
    /* ----------------- misc parameters ----- */  
    dcb.fErrorChar      = 0; 
    dcb.fBinary         = 1;   
    dcb.fNull           = 0;      
    dcb.fAbortOnError   = 0;       
    dcb.wReserved       = 0;    
    dcb.XonLim          = 2;      
    dcb.XoffLim         = 4;      
    dcb.XonChar         = 0x13;    
    dcb.XoffChar        = 0x19;    
    dcb.EvtChar         = 0; 

    if(!SetCommState(hComm,&dcb))
    {
        return FALSE;
    }
    else
        return TRUE;
}

//设置超时时间
bool setTimeOuts(DWORD ReadInterval,DWORD ReadTotalMultiplier,
                 DWORD ReadTotalConstant,DWORD WriteTotalMutiplier,
                 DWORD WriteTotalConstant)
{
    COMMTIMEOUTS timeouts;
    timeouts.ReadIntervalTimeout = ReadInterval;
    timeouts.ReadTotalTimeoutConstant = ReadTotalConstant;
    timeouts.ReadTotalTimeoutMultiplier = ReadTotalMultiplier;
    timeouts.WriteTotalTimeoutConstant = WriteTotalConstant;
    timeouts.WriteTotalTimeoutMultiplier = WriteTotalMutiplier;
    if (!SetCommTimeouts(hComm,&timeouts))
    {
        return FALSE;
    }
    else
        return TRUE;

}
//写进程
DWORD WINAPI MyThread2(LPVOID lParam)
{
    char *m_szWriteBuffer = new char[1000];
    BOOL bWrite = TRUE;
    BOOL bResult = TRUE;
    DWORD dwError= 0;
    DWORD ByteSend = 0;
    HANDLE m_hWriteEvent = NULL;
    ResetEvent(m_hWriteEvent);//将指定的事件对象设置为无信号状态

    while(true)
    {    
        WaitForSingleObject(hSemaphore, INFINITE);// 试图进入信号量关口
        //WaitForSingleObject(hMutex,INFINITE);//等待互斥信号量
//        WaitForSingleObject(hEvent,INFINITE);//等待内核事件对象的通知
        //EnterCriticalSection(&cti);//进入临界区,锁定资源
        Sleep(1500);
        cout<<"写串口->";
        bResult = ClearCommError(hComm,&dwError,&comstat);//清空错误数据
        
            if (bWrite)
            {
                printf("Write:");
                scanf("%s",m_szWriteBuffer);
                bResult = WriteFile(hComm,
                    m_szWriteBuffer,
                    strlen(m_szWriteBuffer)+1,
                    &ByteSend,
                    &m_overlapped);
                if (!bResult)
                {
                    dwError = GetLastError();
                    switch (dwError)
                    {
                    case ERROR_IO_PENDING:
                        {
                            // continue to GetOverlappedResults()
                            bWrite = FALSE;
                            break;
                        }
                    default:
                        {
                            // all other error codes
                            printf("WriteFile()");
                            //break;
                        }
                    }//end switch()
                }//end if(!bResult)

                if (!bWrite)//写入失败
                {

                    bWrite = TRUE;
                    bResult  = GetOverlappedResult(hComm,
                        &m_overlapped,// Overlapped structure
                        &ByteSend,// Stores number of bytes sent
                        TRUE);// Wait flag
                    //deal with the error codes
                    if (!bResult)
                    {
                        printf("GetOVerLappedResult() is writefile()");
                    }
                }//end if(!bWrite)                        
            }//end if(bWrite)    
        //    ReleaseMutex(hMutex);
        //SetEvent(hEvent);//设置事件
        ReleaseSemaphore(hSemaphore,1,0);
        //    LeaveCriticalSection(&cti);//释放临界区
        //    Sleep(2000);
    }//while(true)
    delete m_szWriteBuffer;
    //    return 0;
}

//读进程
DWORD WINAPI MyThread1(LPVOID lParam)
{
    BOOL bRead = TRUE;
    BOOL bResult = TRUE;
    DWORD deError = 0;
    DWORD BytesRead = 0;
    char RecvBuff = '0';

    while(true)
    {
        WaitForSingleObject(hSemaphore, INFINITE);    // 试图进入信号量关口
        //EnterCriticalSection(&cti);                                //进入临界区,锁定资源
        //WaitForSingleObject(hMutex,INFINITE);        //等待互斥信号量
        //WaitForSingleObject(hEvent,INFINITE);            //等待内核事件对象的通知

        Sleep(1000);
        cout<<"读串口->Read :";
        while(true)
        {
            bResult = ClearCommError(hComm,&deError,&comstat);//清空错误数据
            if (comstat.cbInQue == 0)                    //如果输入缓冲区为空
            {
                break;
            }
            if (bRead)
            {
                bResult = ReadFile(hComm,            //串口句柄
                    &RecvBuff,                                //数据接收缓冲区
                    1,                                                //每次读取一个字节
                    &BytesRead,                            //实际读取的数据数量
                    &m_overlapped);                        //异步I/O
                printf("%c",RecvBuff);
                if (!bResult)
                {
                    switch(deError = GetLastError())
                    {
                    case ERROR_IO_PENDING:    //重叠I/O操作进行中
                        bRead = FALSE;
                        break;
                    default:
                        break;
                    }
                }
                else
                    bRead = TRUE;
            }
            if (!bRead)
            {
                bRead = TRUE;

                bResult = GetOverlappedResult(hComm,
                    &m_overlapped
                    ,&BytesRead,
                    TRUE);
            }
        }
        printf("
");
        //        LeaveCriticalSection(&cti);//释放临界区
        //        Sleep(1000);
        //        ReleaseMutex(hMutex);
        //SetEvent(hEvent);//设置事件
        ReleaseSemaphore(hSemaphore,1,0);
    }

    //    return 0;
}
int main()
{
    bool open;
    open = openport(_T("com1"));
    if (open)
    {
        cout<<"openport success"<<endl;
    }
    if (setDCB(9600))
    {
        cout<<"setDCB success"<<endl;
    }
    if (setTimeOuts(0,0,0,0,0))
    {
        cout<<"set timeouts success"<<endl;
    }

    //清空这个串口的所有操作
    PurgeComm(hComm,
        PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
    
    //事件内核对象,可以实现对独占资源的互斥访问
    //    hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    //    SetEvent(hEvent);

    //信号量机制,可以实现对独占资源的互斥访问
    hSemaphore = CreateSemaphore(NULL, 1, 1, NULL);// 创建信号量对象
    
    //临界区,使用临界区方法不能保证两个线程按照一定的顺序执行,只能靠两个线程抢占资源的速度
    //InitializeCriticalSection(&cti);
    
    //创建互斥内核对象,可以实现对独占资源的互斥访问
    //hMutex = CreateMutex(NULL,FALSE,_T("read or write"));

    //创建写串口线程    
    m_hWriteThread = CreateThread(NULL,0,MyThread2,0,0,NULL);

    //创建读串口线程
    m_hReadThread = CreateThread(NULL,0,MyThread1,0,0,NULL);

    //    getchar();
    while (true)
    {

    }

    CloseHandle(m_hReadThread);
    CloseHandle(m_hWriteThread);
    CloseHandle(hMutex);
    CloseHandle(hComm);
    return 0;
}
全部代码
原文地址:https://www.cnblogs.com/songliquan/p/3268108.html