C++线程同步与互斥之互斥体
抢红包练习
第一步:在第一个文本框中输入一个值,比如1000;
第二步:点击抢红包,同时创建3个线程,每个线程循环进行抢红包的操作,每次抢50;
第三步:使用Mutex进行线程控制,当第一个文本框中的值<50时,强红包线程结束.
特别说明:
1、四个文本框中的值总和应该为1000
2、强红包线程每次延时50毫秒.
3、使用WaitForMultipleObjects监听所有线程,当线程全部结束后,调用CloseHandle关闭句柄.
下面是代码实现,我是用MFC实现的。
// MutexExDlg.h : 头文件 // #pragma once // CMutexExDlg 对话框 class CMutexExDlg : public CDialogEx { // 构造 public: CMutexExDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_MUTEXEX_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 static HANDLE hThread[3]; static HANDLE m_Mutex; static DWORD WINAPI ThreadProc0(LPVOID lpParameter); static DWORD WINAPI ThreadProc1(LPVOID lpParameter); static DWORD WINAPI ThreadProc2(LPVOID lpParameter); static DWORD WINAPI ThreadProc3(LPVOID lpParameter); // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnBnClickedButton1(); static int m_Edit0; static int m_Edit1; static int m_Edit2; static int m_Edit3; };
// MutexExDlg.cpp : 实现文件 // #include "stdafx.h" #include "MutexEx.h" #include "MutexExDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMutexExDlg 对话框 CMutexExDlg::CMutexExDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CMutexExDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CMutexExDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT1, m_Edit0); DDV_MinMaxInt(pDX, m_Edit0, 0, 1000); DDX_Text(pDX, IDC_EDIT2, m_Edit1); DDV_MinMaxInt(pDX, m_Edit1, 0, 1000); DDX_Text(pDX, IDC_EDIT3, m_Edit2); DDV_MinMaxInt(pDX, m_Edit2, 0, 1000); DDX_Text(pDX, IDC_EDIT4, m_Edit3); DDV_MinMaxInt(pDX, m_Edit3, 0, 1000); } BEGIN_MESSAGE_MAP(CMutexExDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, &CMutexExDlg::OnBnClickedButton1) END_MESSAGE_MAP() // CMutexExDlg 消息处理程序 BOOL CMutexExDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CMutexExDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CMutexExDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } // 重写虚函数 PreTranslateMessage 屏蔽掉Esc键和Enter键 BOOL CMutexExDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN) { int keyCode = (int)pMsg->wParam; if (keyCode == VK_ESCAPE || keyCode == VK_RETURN) { return TRUE; } } return CDialogEx::PreTranslateMessage(pMsg); } HANDLE CMutexExDlg::m_Mutex = NULL; int CMutexExDlg::m_Edit0 = 0; int CMutexExDlg::m_Edit1 = 0; int CMutexExDlg::m_Edit2 = 0; int CMutexExDlg::m_Edit3 = 0; HANDLE CMutexExDlg::hThread[3] = { NULL }; // 抢红包按钮点击事件处理函数 void CMutexExDlg::OnBnClickedButton1() { // 获取编辑框内容到str变量 CString str; GetDlgItem(IDC_EDIT1)->GetWindowText(str); // CString 转 int m_Edit0 = _ttoi(str); m_Edit1 = 0; m_Edit2 = 0; m_Edit3 = 0; // 这里需要创建一个线程 因为 WaitForMultipleObjects 会阻塞住 另外把this指针作为参数传递给线程,用于子线程更新编辑框内容 HANDLE hThread0 = ::CreateThread(NULL, NULL, ThreadProc0, this, NULL, NULL); CloseHandle(hThread0); } DWORD WINAPI CMutexExDlg::ThreadProc0(LPVOID lpParameter) { // bInitialOwner BOOL,如创建进程希望立即拥有互斥体,则设为TRUE。一个互斥体同时只能由一个线程拥有 // 创建一个互斥体 m_Mutex = ::CreateMutex(NULL, FALSE, L"test"); // 创建三个线程抢红包 hThread[0] = ::CreateThread(NULL, NULL, ThreadProc1, lpParameter, NULL, NULL); hThread[1] = ::CreateThread(NULL, NULL, ThreadProc2, lpParameter, NULL, NULL); hThread[2] = ::CreateThread(NULL, NULL, ThreadProc3, lpParameter, NULL, NULL); // 使用WaitForMultipleObjects监听所有线程,当线程全部结束后,调用CloseHandle关闭句柄. WaitForMultipleObjects(3, hThread, TRUE, -1); ::CloseHandle(hThread[0]); ::CloseHandle(hThread[1]); ::CloseHandle(hThread[2]); ::CloseHandle(m_Mutex); return 0; } // 线程回调函数1 DWORD WINAPI CMutexExDlg::ThreadProc1(LPVOID lpParameter) { CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter; while (true) { WaitForSingleObject(m_Mutex, -1); if (m_Edit0 < 50) { break; } m_Edit0 = m_Edit0 - 50; m_Edit1 = m_Edit1 + 50; // int 转 CString CString str0; str0.Format(_T("%d"), m_Edit0); // 设置编辑框内容 ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT1), str0); CString str1; str1.Format(_T("%d"), m_Edit1); ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT2), str1); Sleep(50); // 释放线程拥有的互斥体的控制权 ReleaseMutex(m_Mutex); } return 0; } // 线程回调函数2 DWORD WINAPI CMutexExDlg::ThreadProc2(LPVOID lpParameter) { CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter; while (true) { WaitForSingleObject(m_Mutex, -1); if (m_Edit0 < 50) { break; } m_Edit0 = m_Edit0 - 50; m_Edit2 = m_Edit2 + 50; CString str0; str0.Format(_T("%d"), m_Edit0); ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT1), str0); CString str1; str1.Format(_T("%d"), m_Edit2); ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT3), str1); Sleep(50); // 释放线程拥有的互斥体的控制权 ReleaseMutex(m_Mutex); } return 0; } // 线程回调函数3 DWORD WINAPI CMutexExDlg::ThreadProc3(LPVOID lpParameter) { CMutexExDlg* pDlg = (CMutexExDlg*)lpParameter; while (true) { WaitForSingleObject(m_Mutex, -1); if (m_Edit0 < 50) { break; } m_Edit0 = m_Edit0 - 50; m_Edit3 = m_Edit3 + 50; CString str0; str0.Format(_T("%d"), m_Edit0); ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT1), str0); CString str1; str1.Format(_T("%d"), m_Edit3); ::SetWindowText(::GetDlgItem(pDlg->m_hWnd, IDC_EDIT4), str1); Sleep(50); // 释放线程拥有的互斥体的控制权 ReleaseMutex(m_Mutex); } return 0; }