多线程编程中的线程同步问题

本文探讨了多线程环境下全局变量的同步问题,并介绍了两种解决方法:使用临界区和设置事件。通过具体代码示例展示了如何正确实现这些同步机制。

  首先来看一个例子,从这个例子我们开始讨论这个问题

void main()
{
    HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL);
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    cout<<g1<<endl;
    cout<<g2<<endl;
}
DWORD  WINAPI ThreadCallBack(LPVOID lParam)  //子线程
{
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    return 0;
}

  在主函数中有一个线程进行for循环累加计算,但是子线程中也在进行计算,这势必会让两个线程的计算会交叉进行,导致最终输出的结果并不是我们想要的。

  在这里我有两种方法解决这个问题:

  一、使用临界区的

  首先解释什么是临界区:在任意时刻只允许一个线程对共享资源进行访问的区域,也就是多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。 如果一个

线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。为了确保读线程读取到的是经过修改的变量,就必须在向变量写入

数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。

CRITICAL_SECTION   cs; //临界区的声明
void main()
{
    InitializeCriticalSection(&cs);       //初始化临界区
    HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL);
    EnterCriticalSection(&cs);       //进入临界区
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    LeaveCriticalSection(&cs);         //执行完出来
    WaitForSingleObject(hThread,INFINITE);     //等子线程执行完
    cout<<g1<<endl;
    cout<<g2<<endl;
    DeleteCriticalSection(&cs);      //销毁临界区
}
DWORD  WINAPI ThreadCallBack(LPVOID lParam)  //子线程
{
    EnterCriticalSection(&cs);
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    LeaveCriticalSection(&cs);
    return 0;
}

  使用临界区的方法之前要声明和初始化临界区。每个线程进入临界区之前,声明一下EnterCriticalSection(&cs)进入临界区,在线程执行完毕之后,LeaveCriticalSection(&cs)退出临界区,接着让其他线程进入临界区。

  在最后所有线程执行完毕时,要销毁临界区。

  二、设置事件

HANDLE  hEvent;  //线程同步  事件
void main()
{
    hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    HANDLE hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadCallBack,NULL,0,NULL);
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    SetEvent(hEvent);      //设置事件信号
    WaitForSingleObject(hThread,INFINITE);     //等子线程执行完
    cout<<g1<<endl;
    cout<<g2<<endl;
    CloseHandle(hEvent);
}
DWORD  WINAPI ThreadCallBack(LPVOID lParam)  //子线程
{
    WaitForSingleObject(hEvent,INFINITE);
    for (int i=0;i<100000;i++)
    {
        g1++;
        g2++;
    }
    return 0;
}    

  首先是创建事件,此时的事件还未受信。当主线程执行完毕时,设置事件受信,子线程中的WaitForSingleObject函数收到信号后,会向下执行。在线程执行完成后,要关闭事件句柄。

  另外有几个小问题:

  一、函数的调用约定

  函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。

  常用的调用约定如下:

  _cdecl

  1、参数是从右向左传递的,也是放在堆栈中。

  2、堆栈平衡是由调用函数来执行的。

  _stdcall

  Win32 API函数绝大部分都是采用__stdcall调用约定的。WINAPI也是__stacall的一个别名。

  1、参数是从右往左传递的,也是放在堆栈中。

  2、函数的堆栈平衡操作是由被调用函数执行的。

  二、句柄是什么?

  句柄是一个标识符,是拿来标识对象或者项目的。

  一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。

  应用程序能够通过句柄访问相应的对象的信息,但是句柄不是指针,程序不能利用句柄来直接阅读文件中的信息。

  如果句柄不在I/O文件中,它是毫无用处的。

  句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows大量使用了句柄来标识对象。

 

转载于:https://www.cnblogs.com/suvllian/articles/5432671.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值