多个线程访问相同资源的时候会产生冲突

解决方案A:原子操作函数 imterpckedIncrement()原子自增函数

解决方案B:

临界区:原子操作只能解决某一个变量,只能使得一个整形变量,但是如果要进行一整段代码,就需要用到临界区

EnterCriticalSection进入临界区

LeaveCriticalSection离开临界区

方案C:

互斥体:

内核对象同步-等待函数原型

WaitForSingleObject()----等待内核对象

WaitForMultipleObjects() ----等待多个内核对象

原子操作

如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构,那么这个操作是一个原子(atomic)操作。

原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分。

将整个操作视作一个整体是原子性的核心特征。

所谓的原子操作其实就是指 当执行该操作的时候必须执行完才能执行别的

临界区

临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机

也就是整段代码都是用原子操作来处理

临界区的资源如果有人在使用,别人就不能使用只能等待

采用原子操作来解决

Windows下的原子操作函数都是Interlocked开头

一下函数调用都是原子操作,即线程安全的:

InterlockedExchange:把目标操作数(参数1所指向的内存中的数)与一个值(参数2)交换,返回参数1的原始值
InterlockedCompareExchange:是把目标操作数(第1参数所指向的内存中的数)与一个值(第3参数)比较,如果相等,则用另一个值(第2参数)与目标操作数(第1参数所指向的内存中的数)交换;返回值为参数1的原始值
InterlockedIncrement:参数所指的内存中的数字加1
InterlockedDecrement:参数所指的内存中的数字减1

InterlockedExchangeAdd:参数1所指的内存中的数字,加上参数2对应的值,返回未加前的参数1
InterlockedExchangePointer:把目标操作数(参数1的指针)设置为参数2对应的指针,返回未重新指向前的参数1指针

采用临界区来解决

在使用临界区之前,需要调用 InitializeCriticalSection() 来初始一个临界区 使用完之后需要调用 DeleteCriticalSection();销毁临界区

创建临界区-InitializeCriticalSection

void InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);

这个函数只需要一个参数,就是一个指向临界体对象的指针

销毁临界区-DeleteCriticalSection

使用完之后需要调用 DeleteCriticalSection();销毁临界区

进入临界区执行EnterCriticalSection

只需要传临界区结构体指针参数

执行完后从临界区出来LeaveCriticalSection

只需要传临界区结构体指针参数

利用临界区

把要使用的代码放在进入临界区和出临界区中就好

互斥体

Windows下的互斥体是一个内核对象,可以跨进程访问

互斥体有两个状态:激发态、非激发态

有一个概念,叫做线程拥有权和临界区类似。

当一个互斥体没有被任何一个线程拥有时,它处于激发态,可以说是缩打开的方式。

当一个线程调用了WaitForSingleObject函数会立刻返回,并将互斥体设置为非激发态,互斥体被锁住,该线程获得拥有权

当互斥体处于非激发态的时候其他线程调用WaitForSingleObject函数无法获得互斥体拥有权

当有拥有去的线程调用ReleaseMutex函数,将互斥体释放。此时互斥体不属于任何线程,处于激发态。

互斥体优点:

互斥体是内核对象,可以跨进程访问

互斥体比较安全,一旦拥有者崩溃,互斥体会立刻释放

创建互斥体-CreateMutex()

互斥体是一个内核对象,所以同样要用内核句柄来创建

HANDLE CreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL                 bInitialOwner,
LPCSTR               lpName
);

参数

第一个参数表示安全描述符

第二个参数表示当前创建互斥体的线程是否是互斥体的拥有者

第三个参数表示互斥体这个对象的名称的指针

利用互斥体

和临界区是差不多的,只是互斥体更加安全,首先要创建互斥体,然后再在要使用的敌法等待互斥体,使用完之后再释放互斥体就好

HANDLE hMutex;
DWORD WINAPI ThreadProc2(LPVOID lpParameter
)
{
for (size_t i = 0; i < 11; i++)
{
WaitForSingleObject(hMutex,-1);
sum++;
ReleaseMutex(&test_critical);
}
return 0;
}
void Create_WinMutex()
{
/*
HANDLE CreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL                 bInitialOwner,
LPCSTR               lpName
);*/
WCHAR MutexName[] = L"SnailGo";
hMutex = CreateMutex(NULL, FALSE, MutexName);
}