windows线程同步之原子锁(原子访问)

原子锁(原子访问):一个线程在访问某个资源的同时必须确保其他线程不会同时访问此资源。

没有实现原子锁的结果:

//定义一个全局变量
long g_lx = 0;
DWORD WINAPI ThreadFunc1(PVOID pvParam){
    for( int index=0; index<10000; index++ )
    {
            g_lx++;           //g_lx加1;
    } 

    return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam){
    for( int index=0; index<10000; index++ )
    {
            g_lx++;           //g_lx加1;
    }
    return 0;
}
 

程序代码欲把g_lx累加到20000;而结果为小于20000. 原因是:g_lx++;不是原子级别的。编译器编译g_lx++的汇编代码近似为:

MOV EAX,[g_lx]               //将g_x的值赋值到EAX寄存器中

INC EAX         //EAX寄存器进行+1操作,原子级别

MOV [g_lx], EAX      //将EAX寄存器中的值赋值到g_lx中

重点是在多个线程同时操作一个资源g_lx ++时,可能会出现如下:

MOV EAX,[g_lx]            //线程1

INC EAX        //线程1

MOV EAX,[g_lx]     //线程2

INC EAX        //线程2

MOV [g_lx], EAX    //线程1

MOV [g_lx], EAX    //线程2

预计g_lx的值是2,但是结果却是1;这就是多个线程同时操作用一个资源时,可能会出现的问题。

================================================================

================================================================

1,如何解决原子方式控制变量,可以使用如下函数:

LONG InterlockedExchangeAdd(
        PLONG volatile PlAddend,      //长整型变量地址
        LONG lIncrement);                 //增长量,如果做减法,可以将此值改成负数。
LONG InterlockedExchangeAdd64(
        PLONGLONG  volatile PlAddend,
        LONGLONG  lIncrement);
//函数的返回值是:返回*plAddend中原来的值 

2,如果变量的增长值是1,也可以使用InterlockedIncrement函数。

如果变量的减少值是1,也可以使用InterlockedDecrement函数。

3,InterlockedExchange和InterlockedExchangePointer函数

LONG InterlockedExchange(
        PLONG volatile plTarget,      //当前值得地址
        LONG lValue);                 //替换值
PVOID InterlockedExchangePointer(
    PVOID* volatile  ppvTarget,
    PVOID pvValue);

这两个函数式将 第二个参数替换第一个参数。应用此函数可以实现旋转锁。下为旋转锁实例:

// 全局变量标示这个资源是否正在被使用
BOOL g_fResourceInUse = FALSE;

void Func1()
{
   //等待访问这个资源,不停的假装告诉系统我正在使用这个资源
   //但是,是通过InterlockedExchange这个原子函数告诉系统的,
   //这意味着如果这个资源正被别的线程使用的时候,CPU不会让这件事变为事实
   //但是呢如果,没线程使用访问改变量的话,当前线程就得逞了。
   //看来主动一些就会有收获的,
   //接下来这个非常主动的线程将要跳出辛苦的循环请求过程享受他的成果了。
   while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)
      Sleep(0);
   //访问刚占有的资源
   //Access the resource.

   //We no longer need to access the resource.
   //我不用了,你拿去玩吧。
   //其实可能有好多和他一样的进程在做一样的事
   InterlockedExchange(&g_fResourceInUse, FALSE);
}

使用旋转锁应该注意的地方:

(1)使用这种同步方式,我们需要保证需要同步的进程是运行在同一优先级的,如果两个线程优先级为4,另一个为3,那么优先级为4的线程不会得到等待资源的机会。

(2)应该避免资源标示和资源本身在同一高速缓存行(高速缓冲区:)

(3)应避免在单处理器的系统上使用旋转锁

4,用于交换的函数:

LONG InterlockedCompareExchange(//返回值:返回原目标值
        LPLONG Destination,          //对目标指针的值
        LONG Exchange,                  //交换值(同目标值交换)
        LONG Comperand );            //比较值(同目标值比较)
PVOID InterlockedCompareExchangePointer(
        PVOID *Destination,
        PVOID Exchange,
        PVOID Comperand );

5,单向链表函数

 
InitializeSListHead 创建一个空栈
InterlockedPushEntrySList 在栈顶添加一个元素
InterlockedPopEntrySList 在栈顶移除一个元素并将它返回
InterlockedFlushSLIst 清空栈
QueryDepthSList 返回栈中元素的数量

windows线程同步之原子锁(原子访问)

时间: 2024-08-27 00:32:28

windows线程同步之原子锁(原子访问)的相关文章

Windows线程同步

说到windows线程同步的方法,有循环锁,临界区(关键代码段),内核对象(Event,Semaphore,Mutex).甚至进程,线程handle等等. 说说用法: 临界区和mutex都可以用于控制共享资源的互斥访问,不同点是 临界区是用户级对象,消耗小,速度快,但是不能跨进程.mutex是内核对象速度慢,但是可以跨进程. semaphore可以用来进行资源控制,信号量的两个关键参数,现有资源数和最大资源数,可以用于对资源的控制,通过ReleaseSemaphore对资源增加,WaitFor*

Windows线程同步总结

Windows线程同步 Windows的线程同步可以利用互斥对象来实现,也可以使用事件对象,关键代码段来实现. 1 事件对象实现线程同步 <1>Event对象创建函数 事件对象的创建事件对象属于内核对象,它包含以三个成员:使用计数,是否是自动重置还是人工重置的布尔值,通知状态的布尔值. HANDLE CreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes, BOOLbManualReset, BOOLbInitialState, LPCSTRlp

[并发并行]_[线程同步]_[Windows用户态下的原子访问的互锁函数]

场景: 1. 多线程编程时,有时候需要统计某个变量或对象的创建个数或者是根据某个变量值来判断是否需要继续执行下去,这时候互锁函数是比较高效的方案之一了. 说明: 1.原子访问:(互锁函数族) -- 原子访问,指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源. -- 调用一个互锁函数通常只需要执行几个CPU周期(通常小于50),并且不需要从用户模式转换为内核模式(通常这需要1000个CPU周期). -- 互锁函数是CPU硬件支持的,如果是x86处理器,互锁函数会向总线发出一个硬

Windows线程同步(未完)

先介绍一个创建线程的API,参考:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx Creates a thread to execute within the virtual address space of the calling process. HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThr

Windows线程同步【2】临界区

临界区是线程同步的一种实现方式.Windows提供了4个关于临界区的函数(InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection,DeleteCriticalSection),要想使用这些函数,必须先有一个临界区变量, CRITICAL_SECTION cs; 临界区变量,不能复制,不能移动,也不能读取里面的字段(可以取临界区变量的地址).总而言之,我们在编写程序时,必须把临界区变量当成一个黑箱,一切对临界区的操作必

windows 线程同步学习测试-1

环境win7旗舰64位系统,vs2013,AMD fx™4100 Auad-core processor ,8G内存, 看<windows核心编程>线程同步一章,看到有说g_x++会不同步的问题,试着写些代码加深印象.发现+1太快了,看不出效果,于是for循环了1亿次.代码如下: #include "stdafx.h" using std::cout; using std::endl; using std::cin; unsigned __stdcall ThreadFun

Windows线程同步【4】读写锁(RWMutex)

在视频播放机程序中,经常采用这样的架构: 一个负责读文件的线程,负责从媒体文件中读取数据并将数据包放入一个包队列(queue),另外有多个线程从这个包队列中取包并解码,然后交给负责显示的线程以便显示图像.于是,便形成了多个线程共享一个队列,一个写,多个只读不写的情形. 在很多时候,我们都会遇到一个写线程(负责写数据),多个读线程(负责读数据)的情形.在这类情形下,当然可以用锁来保证每个线程对共享数据的访问是独占的.但这样的做法在此情形下是低效的,因为它会使得一个读线程在读数据时另外的读线程只能干

windows 线程同步

Windows 临界区,内核事件,互斥量,信号量. 临界区,内核事件,互斥量,信号量,都能完成线程的同步,在这里把他们各自的函数调用,结构定义,以及适用情况做一个总结. 临界区: 适用范围:它只能同步一个进程中的线程,不能跨进程同步.一般用它来做单个进程内的代码快同步,效率比较高. 相关结构:CRITICAL_SECTION  _critical 相关方法: /*初始化,最先调用的函数.没什么好说的,一般windows编程都有类似初始化的方法*/ InitializeCriticalSectio

Windows线程同步【3】互斥锁(Mutex)

我们前面讲过的临界区,如同一个小房间,张三进去了,李四就不能进,如果李四要进,必须等张三出来. 今天我们要讲的互斥锁,像一个物件,这个物件只能同时被一个线程持有.如此一来,便可以通过互斥锁来实现线程的同步. 一.创建 创建互斥锁的方法是调用函数CreateMutex: CreateMutex(&sa, bInitialOwner, szName); 第一个参数是一个指向SECURITY_ATTRIBUTES结构体的指针,一般的情况下,可以是nullptr. 第二个参数类型为BOOL,表示互斥锁创