20170704Windows10_08_临界区、线程静态变量、Slim锁、线程状态

来源:互联网 发布:3d人物软件 编辑:程序博客网 时间:2024/05/21 09:39

临界区及线程函数中使用静态变量:

1:初始化临界区:InitializeCriticalSection()可以初始化临界区。需要传递一个临界区结构体(CRITICAL_SECTION)的参数。他会分配一些内存,他并非是临界区的内存,他只是会和临界区相绑定。在关闭的时候,他不光会将临界区的值清零,还会将申请的内存清理掉。
2:初始化临界区的函数返回值为void。函数在分配内存的时候,他可能会遇到错误。为了解决问题,设计了一个函数:InitializeCriticalSectionAndSpinCount(),它返回BOOL值,可以告诉分配内存是成功还是失败,可以使用他来进行临界区的初始化,并可以返回是否成功。InitializeCriticalSectionAndSpinCount被设计出来叫做:以旋转锁的方式使用临界区。
3:进入临界区:EnterCriticalSection(),进入临界区的函数和我们的死循环有些类似,也是挡住其他线程不能使用已经在使用的。如果进入成功,其他线程访问这个数据就访问不了,他们会进入到等待状态,等待状态是有一个周期的
4:线程从可调度到不可调度,都需要一定的时间,为了节约这段时间,就可以设置里面重试多少次,发现别人在使用,尝试一定次数之后,线程再进入等待状态。
#include <process.h>#include <windows.h>#include <tchar.h>volatile int gNum;volatile int gLoopCount = 100;CRITICAL_SECTION gCs;//临界区,又名关键段。unsigned __stdcall ThreadFun(void* lParam){static int nThreadIndex = 0;EnterCriticalSection(&gCs);//进入临界区nThreadIndex++;gNum = 0;for (int i = 0; i < gLoopCount; ++i){gNum += i;}_tprintf(TEXT("Thread %d: %d\r\n"), nThreadIndex, gNum);LeaveCriticalSection(&gCs);return 0;}int main(){const int MAXTHREADCOUNT = 10;HANDLE hThread[MAXTHREADCOUNT] = { INVALID_HANDLE_VALUE };InitializeCriticalSection(&gCs);//初始化临界区。gCs为临界区的结构体。for (int i = 0; i < MAXTHREADCOUNT; ++i){hThread[i] = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, ThreadFun, nullptr, 0, nullptr));}WaitForMultipleObjects(MAXTHREADCOUNT, hThread, TRUE, INFINITE);for (int i = 0; i < MAXTHREADCOUNT; ++i){CloseHandle(hThread[i]);}DeleteCriticalSection(&gCs);return 0;}

5:线程回调函数里面使用静态变量,他只会被初始化一次,作用域仅在这个线程函数,如果将nThreadIndex++不放在原子操作里面,他就很可能出问题,就会变得不是打印的1,2,……9,10了,打印出来的会很乱。

Slim锁及线程休眠及等待及挂起及阻塞

1:多线程之间的数据同步就是为了避免多个线程在同一时间访问一个数据时带来的错误,一般错误来源:对全局变量的写和读操作,在多线程里面,不停地读是可以的。多个线程读,一个线程修改,就可能出现安全问题(确保写完在读可以)。多条线程写肯定会出现问题。
2:如果业务需求,要求多线程读,一线程写,如果使用旋转锁,就需要对所有线程做同步,所有线程也就变成了一根线程,大大降低了效率。为了解决多线程读一线程写(多线程可同时执行,一个线程特殊,需要隔开)这样的业务,可以使用Slim锁(微型锁)的方式来实现,不仅能够实现功能,还能成倍地提高效率。
3:Slim锁:SRWLOCK gSRW;,只需要初始化,不需要delete。进入Slim锁有两种方式:1:以独占方式进入。2:以共享方式进入。
    1:以独占方式进入锁,就和之前的临界区基本一样,它可以确保下面操作的原子性,并且,在后面需要释放锁。
    2:以共享方式进入锁:必须确保没有以独占方式进入,否则无效,以共享方式打开的话会让其他线程共享。
#include <process.h>#include <windows.h>#include <tchar.h>volatile int gNum;volatile int gLoopCount = 100;SRWLOCK gSRW;//Slim锁。unsigned __stdcall ThreadFun(void* lParam){static int nThreadIndex = 0;AcquireSRWLockExclusive(&gSRW);//以独占方式进入锁,与临界区加锁是一样的,可以保证下面操作的原子性,后面必须释放//AcquireSRWLockShared(&gSRW);//以共享方式进入锁,nThreadIndex++;gNum = 0;for (int i = 0; i < gLoopCount; ++i){gNum += i;}_tprintf(TEXT("Thread %d: %d\r\n"), nThreadIndex, gNum);ReleaseSRWLockExclusive(&gSRW);//释放锁。//LeaveCriticalSection(&gSRW);//释放。return 0;}int main(){const int MAXTHREADCOUNT = 10;HANDLE hThread[MAXTHREADCOUNT] = { INVALID_HANDLE_VALUE };InitializeSRWLock(&gSRW);//初始化Slim锁。for (int i = 0; i < MAXTHREADCOUNT; ++i){hThread[i] = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, ThreadFun, nullptr, 0, nullptr));}WaitForMultipleObjects(MAXTHREADCOUNT, hThread, TRUE, INFINITE);for (int i = 0; i < MAXTHREADCOUNT; ++i){CloseHandle(hThread[i]);}return 0;}

4:临界区里面有一个Try函数,可以尝试是否可以进入,而Slim锁是不可以的,必然会导致某些线程阻塞,变为不可调度。此外,使用AcquireSRWLockExclusive这个函数,是不可以递归的,不能够进入三次,然后再释放三次。
5:休眠、挂起(是一个人为行为,可以认为设定)、阻塞、等待,等名词最终会导致一个事情,线程变得不可调度,这是个好事,它可以释放CPU,降低CPU压力。等待这个状态变为可调度,就会进入可调度区。





原创粉丝点击