第8章 用户模式下的线程同步(3)_Slim读写锁(SRWLock)

8.5 Slim读/写锁(SRWLock)——轻量级的读写锁

(1)SRWLock锁的目的

  ①允许读者线程同一时刻访问共享资源(因为不存在破坏数据的风险)

  ②写者线程应独占资源的访问权,任何其他线程(含写入的线程)要等这个写者线程访问完才能获得资源。

(2)SRWlock锁的使用方法

  ①初始化SRWLOCK结构体 InitializeSRWLock(PSRWLOCK pSRWLock);

  ②写者线程调用AcquireSRWLockExclusive(pSRWLock);以排它方式访问

     读者线程调用AcquireSRWLockShared以共享方式访问

  ③访问完毕后,写者线程调用ReleaseSRWLockExclusive解锁。读者线程要调用ReleaseSRWLockShared解锁

  ④注意SRWLock不需要删除和销毁,所以不用Delete之类的,系统会自动清理。

(3)SRWLock与临界区的不同

  ①不存在TryEnter(Shared/Exclusive)SRWLock之类的函数;如果锁己经被占用,那么调用AcquireSRWLock(Shared/Exclusive)会阻塞调用线程。(如排它写入时锁会被独占)

  ②不能递归获得SRWLock,即一个线程不能为了多次写入资源而多次锁定资源,再多次调用ReleaseSRWLock*来释放锁。

【SRWLock程序】

#include <windows.h>
#include <tchar.h>
#include <locale.h>
#include <time.h>

//////////////////////////////////////////////////////////////////////////
const int g_iThreadCnt = 20;
int g_iGlobalValue = 0;

//////////////////////////////////////////////////////////////////////////
SRWLOCK g_sl = { 0 };

//////////////////////////////////////////////////////////////////////////
DWORD WINAPI ReadThread(PVOID pParam);
DWORD WINAPI WriteThread(PVOID pParam);

//////////////////////////////////////////////////////////////////////////
int _tmain()
{
    _tsetlocale(LC_ALL, _T("chs"));

    srand((unsigned int)time(NULL));
    //读写锁只需初始化,不需要手动释放,系统会自行处理
    InitializeSRWLock(&g_sl);

    HANDLE aThread[g_iThreadCnt];
    DWORD  dwThreadId = 0;
    SYSTEM_INFO si = { 0 };
    GetSystemInfo(&si);

    for (int i = 0; i < g_iThreadCnt;i++){
        if (0 == rand()%2)
            aThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WriteThread, NULL,
                                      CREATE_SUSPENDED,&dwThreadId);
        else
            aThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadThread, NULL,
                                     CREATE_SUSPENDED, &dwThreadId);
        SetThreadAffinityMask(aThread[i],1<<(i % si.dwNumberOfProcessors));
        ResumeThread(aThread[i]);
    }

    //等待所有线程结束
    WaitForMultipleObjects(g_iThreadCnt,aThread,TRUE,INFINITE);

    for (int i = 0; i < g_iThreadCnt;i++){
        CloseHandle(aThread[i]);
    }

    _tsystem(_T("PAUSE"));
    return 0;
}
////////////////////////////////////////////////////////////////////////////
////不加保护的读写
//DWORD WINAPI ReadThread(PVOID pParam)
//{
//    _tprintf(_T("Timestamp[%u]:Thread[ID:0x%x]读取全局变量值为%d\n"),
//             GetTickCount(),GetCurrentThreadId(),g_iGlobalValue);
//    return 0;
//}
//
////////////////////////////////////////////////////////////////////////////
//DWORD WINAPI WriteThread(PVOID pParam)
//{
//
//    for (int i = 0; i <= 4321; i++){
//        g_iGlobalValue = i;
//        //模拟一个时间较长的处理过程
//        for (int j = 0; j < 1000; j++);
//    }
//
//    //我们的要求是写入的最后数值应该为4321,全局变量未被保护
//    //其中线程可能读取0-4321的中间值,这不是我们想要的结果!
//    _tprintf(_T("Timestamp[%u]:Thread[ID:0x%x]写入数据值为%d\n"),
//             GetTickCount(), GetCurrentThreadId(), g_iGlobalValue);
//    return 0;
//}

//////////////////////////////////////////////////////////////////////////
DWORD WINAPI ReadThread(PVOID pParam)
{
    //以共享的访问读
    __try{
        AcquireSRWLockShared(&g_sl);

        //读出来的全局变量要么是0,要么是4321。不可能有其他值
        //当读线程第1个被调度时,会读到0.但一旦写线程被调度,以后所有的
        //读取的值都会是4321
        _tprintf(_T("Timestamp[%u]:Thread[ID:0x%X]读取全局变量值为%d\n"),
                 GetTickCount(), GetCurrentThreadId(), g_iGlobalValue);
    }
    __finally{
        ReleaseSRWLockShared(&g_sl);
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////
DWORD WINAPI WriteThread(PVOID pParam)
{
    //写时以排它的方式
    __try{
        AcquireSRWLockExclusive(&g_sl);
        for (int i = 0; i <= 4321; i++){
            g_iGlobalValue = i;
            SwitchToThread(); //故意切换到其他线程,很明显
                              //在这个循环的期间,因排它方式
                              //所以其它访问锁的线程会被挂起
                              //从而无法读或写。
        }

        //我们的要求是写入的最后数值应该为4321,全局变量未被保护
        //其中线程可能读取0-4321的中间值,这不是我们想要的结果!
        _tprintf(_T("Timestamp[%u]:Thread[ID:0x%X]写入数据值为%d\n"),
                 GetTickCount(), GetCurrentThreadId(), g_iGlobalValue);
    }
    __finally{
        ReleaseSRWLockExclusive(&g_sl);
    }

    return 0;
}

(4)SRWLock锁与其他锁的比较

时间: 2024-08-12 01:54:27

第8章 用户模式下的线程同步(3)_Slim读写锁(SRWLock)的相关文章

第8章 用户模式下的线程同步(4)_条件变量(Condition Variable)

8.6 条件变量(Condition Variables)——可利用临界区或SRWLock锁来实现 8.6.1 条件变量的使用 (1)条件变量机制就是为了简化 “生产者-消费者”问题而设计的一种线程同步机制.其目的让线程以原子方式释放锁并将自己阻塞,直到某一个条件成立为止.如读者线程当没有数据可读取时,则应释放锁并等待,直到写者线程产生了新的数据.同理,当写者把数据结构写满时,那么写者应该释放SRWLock并等待,直到读者把数据结构清空. (2)等待函数:SleepConditionVariab

第8章 用户模式下的线程同步

8.1 原子访问:Interlocked系列函数(Interlock英文为互锁的意思) (1)原子访问的原理 ①原子访问:指的是一线程在访问某个资源的同时能够保证没有其他线程会在同一时刻访问该资源. ②从汇编的角度看,哪怕很简单的一条高级语言都可以被编译成多条的机器指令.在多线程环境下,这条语句的执行就可能被打断.而在打断期间,其中间结果可能己经被其他线程更改过,从而导致错误的结果. ③在Intelx86指令体系中,有些运算指令加上lock前缀就可以保证该指令操作的原子性.其原理是CPU执行该指

Windows核心编程笔记(6)----用户模式下的线程同步

1.原子锁 使用InterlockedExchangeAdd函数来实现原子增长,InterlockedExchange\InterlockedExchangePointer用来交换两个变 量的值,InterlockedCompareExchange对比数值,相等则交换(对应的InterlockedCompareExchangePointer).对应的 还有64位函数. InterlockedIncrement\InterlockedDecrement是比较老的函数,只能增加或递减1,Interl

用户模式下的线程同步

1 /****************************************************************************** 2 Module: Queue.cpp 3 Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre 4 ******************************************************************************/

Windows核心编程笔记(7)----内核模式下的线程同步

1.内核对象同步与用户模式下同步对比 使用内核对象的唯一缺点就是性能,调用内核对象函数时,调用线程必须从用户模式切换到内核模式,这种切换是相当 耗时的. 内核对象(进程.线程.作业)要么处于触发态,要么处于未触发状态.进程内核对象在创建时总是处于未触发状态, 当进程终止时,操作系统会自动使进程内核对象变成触发状态.当进程内核对象处于触发状态后,将永远保持这种状态, 再也不能变回未触发状态. 2.等待内核对象 WaitForSingleObject等待单个内核对象,WaitForMultipleO

线程同步——用户模式下线程同步——Interlocked实现线程同步

1 线程同步分为用户模式下的线程同步和内核对象的线程同步. 2 3 当然用户模式下的线程同步实现速度比内核模式下快,但是功能也有局 4 5 6 7 8 //1.利用原子访问: Interlocked系列函数,关于Interlocked系列函数,我需要知道的是他们,执行的极快 9 10 //(1)首先是InterlockedExchangeAdd兄弟函数, 11 //第一个参数 Addend 代表进行操作数的地址, 12 //第二个参数 Value 代表增加的值,如果是想进行减法,传负数即可 13

CentOS单用户模式下修改ROOT密码和grub加密

Linux 系统处于正常状态时,服务器主机开机(或重新启动)后,能够由系统引导器程序自动引导 Linux 系统启动到多用户模式,并提供正常的网络服务.如果系统管理员需要进行系统维护或系统出现启动异常时,就需要进入单用户模式或修复模式对系统进行管理了. 使用单用户模式有一个前提,就是您的系统引导器(grub)能正常工作,否则要进行系统维护就要使用修复模式. 特注:进入单用户模式,没有开启网络服务,不支持远程连接 Linux 系统中不同的运行级别(Run Level)代表了系统的不同运行状态,例如

线程同步——用户模式下线程同步——Slim读写锁实现线程同步

1 //Slim读/写锁实现线程同步 2 SRWlock 的目的和关键段相同:对同一资源进行保护,不让其它线程访问. 3 但是,与关键段不同的是,SRWlock允许我们区分哪些想要读取资源的线程(读取者线程) 4 和哪些想要更新资源值的线程(写入者线程).让所有读取者资源在同一时刻访问共享资源应该是 5 可行的,这是因为仅仅读取资源并不存在破坏数据的风险.只有当写入者线程想要对资源进行更新时才需要同步. 6 这种情况下,写入者线程应该独占资源访问权:任何线程,无论是读取还是写入者线程,都不许访问

线程同步——用户模式下线程同步——关键段实现线程同步

1 //2.使用关键段实现线程同步 2 使用时必须有以下几个步骤 3 //(1)必须先定义 CRITICAL_SECTION 结构 4 CRITICAL_SECTION g_cs; 5 //(2)初始化关键段 CRITICAL_SECTION 6 InitializeCriticalSection(&g_cs); 7 //(3)在线程中调用 8 DWORD WINAPI ThreadFunOne(PVOID pvParam) 9 { 10 EnterCriticalSection(&g_c