模拟售票大厅实例——多线程时访问共享变量时的安全(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


// CSaleTicketsApp

BEGIN_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_TIME
	enum { IDD = IDD_SALETICKETS_DIALOG };
#endif

	protected:
	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 add
	nTickets = 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 add
	SetDlgItemInt(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);//售票窗口A
		CString 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);//售票窗口A
		CString 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);//售票窗口A
			lbSaleA->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);//售票窗口A
			lbSaleB->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();

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值