首先我们假设一种情况 创建了10个线程 这些线程都执行一个线程函数 (都做一个事)
如 我们设置了是个窗口 每个窗口都负责买火车票 我们的火车票的总数为100
卖完为止
我们定义了一个全局变量
long ticket=100; 为票数
每卖出一张票 票数--
这里我们需要创建了10个线程 这些线程都执行一个线程函数 (买票)
但是 在多个线程同时访问一个共享资源时 会产生并发问题
如: 多个线程同时卖同一张票
那么为了解决并发问题 我么的
解决方案:线程同步
线程同步方式:
1.原子访问:同一时间 只允许一个线程访问资源 volatile防止编译优化,对特殊地址的稳定访问. 直接对内存进行操作
这里想对票数-- 可以用
InterlockedDecrement(&ticket); 他会对票数-- 直接对内存进行操作
同时避免进程并行问题
CRITICAL_SECTION m_cs;
2.临界区(关键段)(用户模式下的线程同步 ):同一时刻只允许一个进程访问代码段
分为:
(1)直接阻塞:如果已经有线程在执行代码段 他就会等待 直到那个线程执行完代码后 锁会随机分配给一个等待的进程
主函数中:
//初始化关键段
//InitializeCriticalSection(&m_cs);
线程函数中:
EnterCriticalSection(&m_cs);
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
LeaveCriticalSection(&m_cs);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
锁的出口
LeaveCriticalSection(&m_cs);
(2)旋转锁:如果已经有线程在执行代码段 他会等待一定的时间(由我们自己决定) 如果等待时间到了 代码段其他线程还是 没执行完 他就会走 不继续等待
这里不好测试 不直观 就不进行演示
(3)非阻塞:如果已经有线程在执行代码段 他就会走 不继续等待
主函数中:
//初始化关键段
//InitializeCriticalSection(&m_cs);
线程函数中:
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
if(!TryEnterCriticalSection(&m_cs))
continue;//走开
//EnterCriticalSection(&m_cs);
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
LeaveCriticalSection(&m_cs);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
LeaveCriticalSection(&m_cs);
}
return 0;
线程函数完整代码如下
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
//原子访问
//InterlockedDecrement(&ticket);//对共享资源执行--操作
//锁的入口 只允许一个进程访问下列关键段
//if(!TryEnterCriticalSection(&m_cs))//
//continue;//走开
//EnterCriticalSection(&m_cs);
//事件的判断 看有没有信号 没信号就走
if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
{
continue;
}
//WaitForSingleObject(m_hMutex,INFINITE);
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
//SetEvent(m_hEvent);
//ReleaseMutex(m_hMutex);
//LeaveCriticalSection(&m_cs);
ReleaseSemaphore(m_hSemaphore,1,NULL);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
//锁的出口
//LeaveCriticalSection(&m_cs);
//信号量
ReleaseSemaphore(m_hSemaphore,1,NULL);
//事件
//SetEvent(m_hEvent);
//互斥量
//ReleaseMutex(m_hMutex);
}
return 0;
}
3.内核对象:同一时刻只允许一个线程访问代码段
(1)互斥量:一直等互斥量 直到等到为止 执行代码
//初始化互斥量
//m_hMutex=CreateMutex(0,//安全属性
// 1,//初始拥有权
// L"MyMutex"//名字
// );
//ReleaseMutex(m_hMutex);
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
WaitForSingleObject(m_hMutex,INFINITE);
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
ReleaseMutex(m_hMutex);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
//互斥量
ReleaseMutex(m_hMutex);
}
return 0;
}
(2)事件 分配事件信号 谁 抢到谁执行
//初始化事件
//m_hEvent=CreateEventA(0,0,0,0);
//SetEvent(m_hEvent);//初始信号为0 把信号设置为1 让线程去抢
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
//事件的判断 看有没有信号 没信号就走
if(WAIT_TIMEOUT==WaitForSingleObject(m_hEvent,10))
{
continue;
}
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
SetEvent(m_hEvent);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
//事件
SetEvent(m_hEvent);
}
return 0;
(3)信号量:允许同一时间内指定数量的线程访问代码段
//初始化信号量
m_hSemaphore=CreateSemaphoreW(0,//安全属性
1,//初始化信号量总数
10,//最大信号量总数
0//名字
);
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
//信号的判断 看有没有信号 没信号就走
if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
{
continue;
}
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
ReleaseSemaphore(m_hSemaphore,1,NULL);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
//信号量
ReleaseSemaphore(m_hSemaphore,1,NULL);
}
return 0;
}
上述三种内核对象需要在最后句柄释放
if(m_hMutex)
{
CloseHandle(m_hMutex);
m_hMutex=0;
}
if(m_hEvent)
{
CloseHandle(m_hEvent);
m_hEvent=0;
}
if(m_hSemaphore)
{
CloseHandle(m_hSemaphore);
m_hSemaphore=0;
}
我们在这里实现了单例模式
//单例
HANDLE hdl=OpenMutexW(MUTEX_ALL_ACCESS,0,L"MyMutex");
if(hdl)
{
qDebug()<<"app is exists";
exit(0);
}
完整代码如下
#include <QCoreApplication>
#include <windows.h>
#include<QDebug>
long ticket=100;
CRITICAL_SECTION m_cs;// 临街
HANDLE m_hMutex;//互斥量
HANDLE m_hEvent;//事件
HANDLE m_hSemaphore;//信号量
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
while(1)
{
if(ticket==0)
{
printf("Thread id[%d] go\n",GetCurrentThreadId());
break;
}
Sleep(10);
//原子访问
//InterlockedDecrement(&ticket);//对共享资源执行--操作
//锁的入口 只允许一个进程访问下列关键段
//if(!TryEnterCriticalSection(&m_cs))//
//continue;//走开
//EnterCriticalSection(&m_cs);
//事件的判断 看有没有信号 没信号就走
if(WAIT_TIMEOUT==WaitForSingleObject(m_hSemaphore,10))
{
continue;
}
//WaitForSingleObject(m_hMutex,INFINITE);
if(ticket==0)
{
printf("Thread id[%d] go-------\n",GetCurrentThreadId());
//SetEvent(m_hEvent);
//ReleaseMutex(m_hMutex);
//LeaveCriticalSection(&m_cs);
ReleaseSemaphore(m_hSemaphore,1,NULL);
break;
}
ticket--;
printf("Thread id[%d] ticket %d\n",GetCurrentThreadId(),ticket);
//锁的出口
//LeaveCriticalSection(&m_cs);
//信号量
ReleaseSemaphore(m_hSemaphore,1,NULL);
//事件
//SetEvent(m_hEvent);
//互斥量
//ReleaseMutex(m_hMutex);
}
return 0;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//单例
HANDLE hdl=OpenMutexW(MUTEX_ALL_ACCESS,0,L"MyMutex");
if(hdl)
{
qDebug()<<"app is exists";
exit(0);
}
//初始化信号量
m_hSemaphore=CreateSemaphoreW(0,//安全属性
1,//初始化信号量总数
10,//最大信号量总数
0//名字
);
//初始化事件
//m_hEvent=CreateEventA(0,0,0,0);
//SetEvent(m_hEvent);//初始信号为0 把信号设置为1 让线程去抢
//初始化关键段
//InitializeCriticalSection(&m_cs);
//初始化互斥量
//m_hMutex=CreateMutex(0,//安全属性
// 1,//初始拥有权
// L"MyMutex"//名字
// );
//ReleaseMutex(m_hMutex);
HANDLE han=NULL;
for(int i=0;i<10;i++)
{
han=CreateThread(0,0,&ThreadProc,0,0,0);
if(han)
{
CloseHandle(han);
han=0;
}
}
a.exec();
//销毁关键段
//DeleteCriticalSection(&m_cs);
//释放内核对象 互斥量/事件
if(m_hMutex)
{
CloseHandle(m_hMutex);
m_hMutex=0;
}
if(m_hEvent)
{
CloseHandle(m_hEvent);
m_hEvent=0;
}
if(m_hSemaphore)
{
CloseHandle(m_hSemaphore);
m_hSemaphore=0;
}
return a.exec();
}