模拟售票大厅实例——多线程时访问共享变量时的安全(CMutex或CCriticalSection的应用)
当程序运行时,可以通过多线程来提高程序运行的效率和拥有更好的体验。但多线程(或多进程)同时也带来很多的问题:最严重的莫过于对同一个对象或变量访问时,由于线程运行异步的原因,会造成程序运行出现无法控制的重大错误。对此,MFC有控制线程或进程同步的封装类:如CMutex或CCriticalSection等等。详细用法不说,直接上代码:
源码:https://download.csdn.net/download/weixin_44027440/11428840
效果图:
【SaleTickets.h】
// SaleTickets.h: PROJECT_NAME 应用程序的主头文件
//#pragma once#ifndef __AFXWIN_H__#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif#include "resource.h" // 主符号// CSaleTicketsApp:
// 有关此类的实现,请参阅 SaleTickets.cpp
//class CSaleTicketsApp : public CWinApp
{
public:CSaleTicketsApp();// 重写
public:virtual BOOL InitInstance();// 实现DECLARE_MESSAGE_MAP()
};extern CSaleTicketsApp theApp;
【SaleTickets.cpp】
// SaleTickets.cpp: 定义应用程序的类行为。
//#include "stdafx.h"
#include "SaleTickets.h"
#include "SaleTicketsDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// CSaleTicketsAppBEGIN_MESSAGE_MAP(CSaleTicketsApp, CWinApp)ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()// CSaleTicketsApp 构造CSaleTicketsApp::CSaleTicketsApp()
{// TODO: 在此处添加构造代码,// 将所有重要的初始化放置在 InitInstance 中
}// 唯一的 CSaleTicketsApp 对象CSaleTicketsApp theApp;// CSaleTicketsApp 初始化BOOL CSaleTicketsApp::InitInstance()
{CWinApp::InitInstance();// 创建 shell 管理器,以防对话框包含// 任何 shell 树视图控件或 shell 列表视图控件。CShellManager *pShellManager = new CShellManager;// 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));// 标准初始化// 如果未使用这些功能并希望减小// 最终可执行文件的大小,则应移除下列// 不需要的特定初始化例程// 更改用于存储设置的注册表项// TODO: 应适当修改该字符串,// 例如修改为公司或组织名SetRegistryKey(_T("应用程序向导生成的本地应用程序"));CSaleTicketsDlg dlg;m_pMainWnd = &dlg;INT_PTR nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: 在此放置处理何时用// “确定”来关闭对话框的代码}else if (nResponse == IDCANCEL){// TODO: 在此放置处理何时用// “取消”来关闭对话框的代码}else if (nResponse == -1){TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");}// 删除上面创建的 shell 管理器。if (pShellManager != nullptr){delete pShellManager;}#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)ControlBarCleanUp();
#endif// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,// 而不是启动应用程序的消息泵。return FALSE;
}
【SaleTicketsDlg.h】
// SaleTicketsDlg.h: 头文件
//#pragma once// CSaleTicketsDlg 对话框
class CSaleTicketsDlg : public CDialogEx
{
// 构造
public:CSaleTicketsDlg(CWnd* pParent = nullptr); // 标准构造函数// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_SALETICKETS_DIALOG };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持// 实现
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()public:static UINT SaleAProc(LPVOID pParam);static UINT SaleBProc(LPVOID pParam);static UINT AutoSaleAProc(LPVOID pParam);static UINT AutoSaleBProc(LPVOID pParam);afx_msg void OnBnClickedButtonSaleA();afx_msg void OnBnClickedButtonSaleB();afx_msg void OnBnClickedButtonAutosale();CWinThread *m_pSaleAThread;CWinThread *m_pSaleBThread;afx_msg void OnBnClickedButtonReset();
};
【SaleTicketsDlg.cpp】
// SaleTicketsDlg.cpp: 实现文件
//#include "stdafx.h"
#include "SaleTickets.h"
#include "SaleTicketsDlg.h"
#include "afxdialogex.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif//全局变量
int nTickets;
CMutex g_mutex(FALSE, _T("Tickets"));
//CCriticalSection g_mutex;// CSaleTicketsDlg 对话框CSaleTicketsDlg::CSaleTicketsDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_SALETICKETS_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);//fdd addnTickets = TICKETSNUM;}void CSaleTicketsDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CSaleTicketsDlg, CDialogEx)ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_SALE_A, &CSaleTicketsDlg::OnBnClickedButtonSaleA)ON_BN_CLICKED(IDC_BUTTON_SALE_B, &CSaleTicketsDlg::OnBnClickedButtonSaleB)ON_BN_CLICKED(IDC_BUTTON_AUTOSALE, &CSaleTicketsDlg::OnBnClickedButtonAutosale)ON_BN_CLICKED(IDC_BUTTON_RESET, &CSaleTicketsDlg::OnBnClickedButtonReset)
END_MESSAGE_MAP()// CSaleTicketsDlg 消息处理程序BOOL CSaleTicketsDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动// 执行此操作SetIcon(m_hIcon, TRUE); // 设置大图标SetIcon(m_hIcon, FALSE); // 设置小图标// TODO: 在此添加额外的初始化代码//fdd addSetDlgItemInt(IDC_EDIT_TICKETS, nTickets);return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。void CSaleTicketsDlg::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 CSaleTicketsDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}UINT CSaleTicketsDlg::SaleAProc(LPVOID pParam) {//解读传送的变量CWnd *pDlg = (CWnd *)pParam;//上锁g_mutex.Lock();if (nTickets > 0) {Sleep(1);CString strName;pDlg->GetDlgItemText(IDC_EDIT_NAME_A, strName);//购票人姓名CListBox *lbSaleA = (CListBox *)pDlg->GetDlgItem(IDC_LIST_NAME_A);//售票窗口ACString s;s.Format(_T("%d--->"), TICKETSNUM+1 - nTickets);s += strName;lbSaleA->AddString(s);pDlg->SetDlgItemInt(IDC_EDIT_TICKETS, --nTickets);}else {AfxMessageBox(_T("你来晚了,票已经卖完了!"));}//解锁g_mutex.Unlock();return 0;}
UINT CSaleTicketsDlg::SaleBProc(LPVOID pParam) {//解读传送的变量CWnd *pDlg = (CWnd *)pParam;//上锁g_mutex.Lock();if (nTickets > 0) {Sleep(1);CString strName;pDlg->GetDlgItemText(IDC_EDIT_NAME_B, strName);//购票人姓名CListBox *lbSaleB = (CListBox *)pDlg->GetDlgItem(IDC_LIST_NAME_B);//售票窗口ACString s;s.Format(_T("%d--->"), TICKETSNUM+1 - nTickets);s += strName;lbSaleB->AddString(s);pDlg->SetDlgItemInt(IDC_EDIT_TICKETS, --nTickets);}else {AfxMessageBox(_T("你来晚了,票已经卖完了!"));}//解锁g_mutex.Unlock();return 0;}void CSaleTicketsDlg::OnBnClickedButtonSaleA()
{// TODO: 在此添加控件通知处理程序代码m_pSaleAThread = AfxBeginThread(SaleAProc,(LPVOID)this);
}void CSaleTicketsDlg::OnBnClickedButtonSaleB()
{// TODO: 在此添加控件通知处理程序代码m_pSaleAThread = AfxBeginThread(SaleBProc, (LPVOID)this);
}void CSaleTicketsDlg::OnBnClickedButtonAutosale()
{// TODO: 在此添加控件通知处理程序代码m_pSaleAThread=AfxBeginThread(AutoSaleAProc, (LPVOID)this);m_pSaleBThread = AfxBeginThread(AutoSaleBProc, (LPVOID)this);}UINT CSaleTicketsDlg::AutoSaleAProc(LPVOID pParam) {//解读传送的变量CWnd *pDlg = (CWnd *)pParam;while (1) {g_mutex.Lock();if (nTickets > 0) {Sleep(1);CString strName;strName.Format(_T("%d--->姓名:无"), TICKETSNUM+1 - nTickets);CListBox *lbSaleA = (CListBox *)pDlg->GetDlgItem(IDC_LIST_NAME_A);//售票窗口AlbSaleA->AddString(strName);pDlg->SetDlgItemInt(IDC_EDIT_TICKETS, --nTickets);g_mutex.Unlock();}else{AfxMessageBox(_T("窗口A提示您:票已经卖完了!"));g_mutex.Unlock();break;}}return 0;
}
UINT CSaleTicketsDlg::AutoSaleBProc(LPVOID pParam) {//解读传送的变量CWnd *pDlg = (CWnd *)pParam;while (1) {g_mutex.Lock();if (nTickets > 0) {Sleep(1);CString strName;strName.Format(_T("%d--->姓名:无"), TICKETSNUM+1 - nTickets);CListBox *lbSaleB = (CListBox *)pDlg->GetDlgItem(IDC_LIST_NAME_B);//售票窗口AlbSaleB->AddString(strName);pDlg->SetDlgItemInt(IDC_EDIT_TICKETS, --nTickets);g_mutex.Unlock();}else{AfxMessageBox(_T("窗口B提示您:票已经卖完了!"));g_mutex.Unlock();break;}}return 0;
}void CSaleTicketsDlg::OnBnClickedButtonReset()
{// TODO: 在此添加控件通知处理程序代码nTickets = TICKETSNUM;SetDlgItemInt(IDC_EDIT_TICKETS, nTickets);SetDlgItemText(IDC_EDIT_NAME_A, _T(""));SetDlgItemText(IDC_EDIT_NAME_B, _T(""));((CListBox *)GetDlgItem(IDC_LIST_NAME_A))->ResetContent();((CListBox *)GetDlgItem(IDC_LIST_NAME_B))->ResetContent();}
模拟售票大厅实例——多线程时访问共享变量时的安全(CMutex或CCriticalSection的应用)相关推荐
- java多线程同步 多窗口卖票实例_java多线程之火车售票系统模拟实例
1.前言 为了学习多线程共享与通信,我们模拟一个火车售票系统,假设有10张火车票,三个窗口(也就是三个线程)同时进行售票. 2.非同步代码 package com.tl.skyLine.thread; ...
- 利用JAVA多线程模拟售票系统,对统一资源进行处理
首先多线程处理统一资源的方式有两种 分别介绍一下两者的区别 synchronized与Lock的区别 1.Lock不是Java语言内置的,synchronized是Java语言的关键字 2.synch ...
- Java模拟售票窗口代码_java多线程模拟售票,多个窗口售票
package com.ma.thread001; /** * 多线程模拟售票,多个窗口售票 * @author ma * */ public class SellTicktDemo implemen ...
- servlet单实例多线程 ---线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。(所有建议不要在servlet中定义成员变
Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servl ...
- servlet单实例多线程模式
Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在 ...
- java 模拟火车站售票系统_模拟售票系统java编程
模拟售票系统java编程 /* 项目:用多线程设计一个模拟火车站售票大厅的工作情形. 问题描述:火车站有许多售票窗口,有些开放,有些不开放.顾客进入火车站售票厅后,到某个售票窗口排队等候,排到了就办理 ...
- 操作系统:模拟售票功能
以下程序是模拟售票功能,使用临界区对象,其中SellPro_1,SellPro_2两个函数分别对应两个售票进程,一次售出一张票. //模拟售票程序 #include <windows.h> ...
- JSP —— Servlet 单实例多线程模式
转载自:http://kakajw.iteye.com/blog/920839 前言:Servlet/JSP技术和ASP.PHP等相比,由于其多线程运行而具有很高的执行效率.由于Servlet/JSP ...
- java 学习~多线程通信 使用共享变量 例子和解释
多线程互相通信一般使用共享变量.. 完整验证代码: main 方法 public class Test5 {public static void main(String[] args) {Datax ...
最新文章
- 堡垒机jumpserver集群部署
- oracle celient 作用,WebLogic Server 10.3 SSL配置及SSL协议传输的WebSevice调用.doc
- java实现 k nn算法_数据挖掘(二)——Knn算法的java实现
- Centos 6.0/ Nginx 安装与配置
- 02.生成、打包、部署和管理应用程序及类型
- [转]用C#编写ActiveX控件(一)
- C++/C语言实现HTTP的GET和POST请求
- go语言 panic
- iOS开发中常用的宏
- 如何使用SVG生成超酷的页面预加载素描动画效果
- 代码雨代码源复制_黑色帝国中代码雨如何实现?用python就可以了
- 安装ie9提示未能完成安装_升级Internet Explorer未能完成安装四种解决措施
- 基于3D人像复原技术的试衣平台
- Python自动采集微信联系人
- 2021-06-22 加水印后原本EXCEL内容被覆盖
- 电脑C盘怎么清理到最干净
- ping命令TTL的意思
- 互斥 互斥的解决方案
- 专访Alasend万能登陆器创作团队
- ETL工具之Informatica