今天要写的小程序是卖票
结果如下:共100张票,10个线程
需要声明的一些变量:
public: volatile int m_tickets;//原子访问 static unsigned __stdcall ThreadProc(void* lparam); CListBox m_lstbox; map<unsigned int , int> IDMap;//将线程ID和0~10对应 bool m_flag; CRITICAL_SECTION m_cs;//临界区 HANDLE m_Mutex;//互斥量
按钮的处理函数:
void CSoldTicketDlg::OnBnClickedButton1() { unsigned int IDnum; for(int i = 0; i < 10; i++) { _beginthreadex(NULL,0,&ThreadProc,this,0,&IDnum); IDMap[IDnum] = i; } }
线程同步有三种方式:
一 . 原子访问:指的是一个线程在访问某个资源的同时,能够保证没有其他线程会在同一时刻访问同一资源。Interlocked系列。
volatile,:防止编译优化(从寄存器中取值,相对于从外存取值更节省时间,但造成了不能取到的值不能及时更新),对特殊地址的稳定访问。
缺点:只能实现对一个32位或者64位值的单独访问,不能实现复杂的功能。
二 . 关键段(临界区):同一时刻只一个线程访问代码段
1.普通锁
IniticalizeCriticalsection(&g_cs); //初始化CRITICAL_SECTION 中的成员变量
EnterCriticalSection(&g_cs); //线程用它来检查占用标志的函数
LeaveCriticalSection(&g_cs); //离开“卫生间”,门上重新置为无人
DeleteCriticalsection(&g_cs); //删除事件的内核对象,以及为CRITICAL_SECTION初始化的资源。
初始化函数:InitializeCriticalSection(&m_cs);
在Destroy函数中:
void CSoldTicketDlg::OnDestroy() { CDialogEx::OnDestroy(); DeleteCriticalSection(&m_cs); }
线程执行的函数:
unsigned __stdcall CSoldTicketDlg::ThreadProc(void* lparam) { CSoldTicketDlg* pthis = (CSoldTicketDlg*)lparam; int num = pthis->IDMap[GetCurrentThreadId()]; while(1) { if(pthis->m_tickets <= 0 ) break; Sleep(100); CString str; EnterCriticalSection(&pthis->m_cs);//锁住 if(pthis->m_tickets > 0)//解决负数问题 { str.Format(_T("第%d窗口,卖出第%d张票"),num , pthis->m_tickets--); } LeaveCriticalSection(&pthis->m_cs); pthis->m_lstbox.AddString(str); } return 0; }
2.旋转锁:
InitializeCriticalSectionAndSpinCount:旋转锁不断循环,尝试在一段时间内获得访问权。只有当尝试失败的时候,线程才会切换到内核模式并进入等待状态。
SetCriticalSectionSpinCount( //改变旋转锁的次数
在初始化函数中:InitializeCriticalSectionAndSpinCount(&m_cs,1);
3.异步
TryEnterCriticalSection:线程在访问时,如果不能访问资源,那么它继续做其他事情,而不用等待。
将EnterCriticalSection换为
if(!TryEnterCriticalSection(&pthis->m_cs))//异步
continue;
三 . 互斥量
初始化函数:m_Mutex = CreateMutex(0,FALSE,0);//第二个参数为创建互斥量的进程没有优先级
unsigned __stdcall CSoldTicketDlg::ThreadProc(void* lparam) { CSoldTicketDlg* pthis = (CSoldTicketDlg*)lparam; int num = pthis->IDMap[GetCurrentThreadId()]; while(1) { if(pthis->m_tickets <= 0 ) break; Sleep(100); CString str; if(WAIT_TIMEOUT == WaitForSingleObject(pthis->m_Mutex,50)) continue; if(pthis->m_tickets > 0)//解决负数问题 { str.Format(_T("第%d窗口,卖出第%d张票"),num , pthis->m_tickets--); } ReleaseMutex(pthis->m_Mutex); pthis->m_lstbox.AddString(str); } return 0; }