VC6.0实现多线程——以火车票售卖为例

最近在学习C++的过程中了解到多线程,发现“互斥锁”很有用,想着实现一下,无奈对linux系统不是很熟悉,只能在windows上借助VC++6.0试着实现。过程中因为数据类型转换等问题一直做的磕磕绊绊,最终也只是搞出来一个框架,还有很多细节有待完善。先总结下来,留着以后继续探索。

问题说明

  • 主要讨论火车票的售卖问题。同一时刻会有不同的窗口卖票,如果不对各个窗口的操作进行管理,后台车票数据会发生错误。为解决这一问题,可以引入“互斥锁”,具体操作为:如果某个窗口想修改后台数据,需要首先获得操作权限(即获得互斥锁);在互斥锁被某个窗口锁定之后,该窗口可以修改数据;待窗口对数据的操作完成后,释放互斥锁。这时其他请求互斥锁的窗口线程可以进行跟上面类似的操作。注意,这里的请求互斥锁操作用到了函数WaitForSingleObject(),该函数等待一个互斥锁,如果不能立即获得该互斥锁,将进入休眠等待状态,此时只消耗很少的CPU时间。WaitForSingleObject() 支持队列,即同一时刻会有多个线程在请求互斥锁,优先发起请求的线程也将优先获得互斥锁。
  • 从上面的分析可以看出,引入“互斥锁”可以保证某一时刻只有一个线程在操作数据,从而防止数据错误。
  • 程序主要分三部分,分别是主程序入口(main函数)、头文件(声明需要用到的结构体和线程入口函数)以及线程入口函数的实现。

程序实现

  1. ticketsSale.h —— 头文件
#include <windows.h>
#include <string>
#include <iostream>

using namespace std;
#define NameLength 20		//车票名字最长为20个字符

//定义结构体用于系统中存储的车票数量和车票名称
typedef struct _Tickets
{
	int tCount;
	char TicketsName[NameLength];

	//结构体对象的默认初始化
	_Tickets():tCount(0)	//数量默认初始化为0
	{
		memset(TicketsName,0,NameLength*(sizeof(char)))	;
	}

}TICKETS;		//结构体的别名为TICKETS

//定义线程入口函数的参数结构体
typedef struct _Thread
{
	TICKETS *ptickets;		//当前系统中的车票信息
	char threadName[NameLength];		//线程名

    _Thread():ptickets(NULL)	//车票信息(结构体对象)初始化为空NULL
    {
        memset(threadName, 0, NameLength * sizeof(char));
    }
}THREADS;		//结构体别名为THREADS

//线程入口函数
DWORD WINAPI SaleTicket(LPVOID lpParameter);		//注意线程函数的定义规范
  1. SaleTicket.cpp —— 线程入口函数
#include <windows.h>
#include <string>
#include <iostream>
#include "ticketsSale.h"

using namespace std;

extern HANDLE g_hMutex;		//外部互斥锁

//线程入口函数
DWORD WINAPI SaleTicket(LPVOID lpParameter)		//注意线程函数的定义规范
{

	// 对传入的参数进行强制类型转换,由无类型指针变为整形数指针
	THREADS * pthread = (THREADS *)lpParameter;  

	//需要处理的车票信息,用指针获取
	//这里用指针获取车票信息,是为了保证多线程之间对车票的处理可以同步
	TICKETS * psaletickets = pthread->ptickets; 

	while(psaletickets->tCount>0)
	{
		//请求获得一个互斥量锁,等待时间为infinite
        WaitForSingleObject(g_hMutex, INFINITE);

		//在获得互斥锁后,车票数量可能已经发生更改,需要重新查询、判断
		if((psaletickets->tCount)>0)
		{
			cout << pthread->threadName << "售出"<< psaletickets->TicketsName<< "的票;";
			cout << "车票编号:" << psaletickets->tCount<<endl;
			psaletickets->tCount--;		//修改后台剩余车票信息
			cout << "剩余车票 "<<psaletickets->tCount << "张 "<<endl;
			cout <<endl;
		}
		else		//已经没有车票
		{
			cout << "出票失败,已经没有余票! "<<endl;
		}
	    Sleep(100);
	    
        //操作完成,释放互斥量锁
        ReleaseMutex(g_hMutex);
	}
	
	return 0;

}
  1. main.cpp —— 主程序入口
#include <windows.h>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <sstream>
#include "ticketsSale.h"

using namespace std;

HANDLE g_hMutex;	//这是一个全局变量,在main函数外面声明才能在其他地方引用

int main()
{
	//创建一个互斥量用于线程间同步
	g_hMutex = CreateMutex(NULL, FALSE, NULL);

	//初始化火车票信息
    TICKETS ticket;
    ticket.tCount = 100;
    strcpy(ticket.TicketsName, "北京-->赣州");

	const int ThreadsNum = 5;		//共有5个线程可以操作数据

	//数组声明中,下标必须为常量,因此ThreadsNum声明为const
	THREADS threadSale[ThreadsNum];		//线程结构体数组 
    HANDLE hThread[ThreadsNum];		//用于保存创建线程后的返回句柄

	//依次创建5个线程
	for(int i =0;i<ThreadsNum;i++)
	{
		//构建线程入口函数需要传入的参数结构体
		(threadSale[i]).ptickets = &ticket;	

		//用stringstream流实现数字转字符串
		stringstream s;
		s << i;
		string name = "窗口"+s.str();

		strcpy(threadSale[i].threadName,name.c_str());

		//创建线程
        hThread[i] = CreateThread(NULL, NULL, SaleTicket, &threadSale[i], 0, NULL);
	}

	for(int j = 0;j<ThreadsNum;j++)
	{	        
		//关闭线程
        CloseHandle(hThread[j]);
	}

	system("pause");
	return 0;
}
  1. 运行结果
窗口0售出北京-->赣州的票;车票编号:100
剩余车票 99张

请按任意键继续. . . 窗口1售出北京-->赣州的票;车票编号:99
剩余车票 98张

窗口2售出北京-->赣州的票;车票编号:98
剩余车票 97张

窗口3售出北京-->赣州的票;车票编号:97
剩余车票 96张

窗口4售出北京-->赣州的票;车票编号:96
剩余车票 95张

窗口0售出北京-->赣州的票;车票编号:95
剩余车票 94
  1. 大致实现了多线程同步的功能,可是main线程和各个子线程是一起执行的,跟想要的结果不一样。理想情况下,main线程应该独立于子线程。怎么做到,留着以后进一步探索喽!
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值