VC6.0实现多线程——以火车票售卖为例
作者:互联网
最近在学习C++的过程中了解到多线程,发现“互斥锁”很有用,想着实现一下,无奈对linux系统不是很熟悉,只能在windows上借助VC++6.0试着实现。过程中因为数据类型转换等问题一直做的磕磕绊绊,最终也只是搞出来一个框架,还有很多细节有待完善。先总结下来,留着以后继续探索。
问题说明
- 主要讨论火车票的售卖问题。同一时刻会有不同的窗口卖票,如果不对各个窗口的操作进行管理,后台车票数据会发生错误。为解决这一问题,可以引入“互斥锁”,具体操作为:如果某个窗口想修改后台数据,需要首先获得操作权限(即获得互斥锁);在互斥锁被某个窗口锁定之后,该窗口可以修改数据;待窗口对数据的操作完成后,释放互斥锁。这时其他请求互斥锁的窗口线程可以进行跟上面类似的操作。注意,这里的请求互斥锁操作用到了函数WaitForSingleObject(),该函数等待一个互斥锁,如果不能立即获得该互斥锁,将进入休眠等待状态,此时只消耗很少的CPU时间。WaitForSingleObject() 支持队列,即同一时刻会有多个线程在请求互斥锁,优先发起请求的线程也将优先获得互斥锁。
- 从上面的分析可以看出,引入“互斥锁”可以保证某一时刻只有一个线程在操作数据,从而防止数据错误。
- 程序主要分三部分,分别是主程序入口(main函数)、头文件(声明需要用到的结构体和线程入口函数)以及线程入口函数的实现。
程序实现
- 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); //注意线程函数的定义规范
- 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;
}
- 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;
}
- 运行结果
窗口0售出北京-->赣州的票;车票编号:100
剩余车票 99张
请按任意键继续. . . 窗口1售出北京-->赣州的票;车票编号:99
剩余车票 98张
窗口2售出北京-->赣州的票;车票编号:98
剩余车票 97张
窗口3售出北京-->赣州的票;车票编号:97
剩余车票 96张
窗口4售出北京-->赣州的票;车票编号:96
剩余车票 95张
窗口0售出北京-->赣州的票;车票编号:95
剩余车票 94张
- 大致实现了多线程同步的功能,可是main线程和各个子线程是一起执行的,跟想要的结果不一样。理想情况下,main线程应该独立于子线程。怎么做到,留着以后进一步探索喽!
标签:include,窗口,函数,VC6.0,为例,互斥,线程,车票,多线程 来源: https://blog.csdn.net/sleeping2dogs/article/details/99641469