引入:
在这个系列的第一篇博文中介绍了线程的基本概念
这篇文章就立刻投入实战,
讲一讲怎么样写一个简单的多线程并发的抢票程序
也就是说 用多个线程对临界区数据的抢占
来模拟多个人不排队在售票窗口抢票的场景
首先我们会介绍和线程相关的api
然后用一个小例子介绍这些api的使用方法
最后就是讲解这个"抢票程序"
目录
基本API
需要包括的头文件
#include<pthread .h>
线程创建函数
int pthread_create (pthread_ t *thread , canst pthread_attr_ t *attr, void * (*start_routine) (void *) , void *arg) ;
第一个参数 传入参数 若线程创建成功 则该参数赋值指向线程标识符的指针 指向内存单元将存放线程id 若创建线程失败 则该参数未定义
第二个 数用来设置线程属性,
第三个参数是线程运行函数的起始地址,
最后一个参数是运行函数的参数
返回值:若线程创建成功,则返回 0;若线程创建失败,则返回出错编号,
主线程回收子线程函数
int pthread_join (pthread_t thread , void ** retval)
第一个参数为被 待的线程标识符,
第二个参数为一个用户定义的指针,用来获取被等待线程的结束时候传进来的返回值
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止, 函数返回时,被等待线程的资源被收回
子线程自我结束函数
pthread_exit 函数, 一个线程的结束有两种途径:
(1)函数已 结束,调用它的线程也就结束了;
(2)通过函数 pthread exit 来实现
它的函数原型为void pthread_exit (void *retval) ;
它的参数就是他要给主线程返回的值
获得线程id
设置线程属性
基本实例
线程相关API的基本使用
#include<stdio.h>
#include<pthread.h>
#include<iostream>
#include<string>
using namespace std;
struct thread_info{
unsigned long thread_id;
string thread_name;
};
void* thread_func(void* argv )
{
cout<<"now in thread...."<<endl;
cout<<"main thread name:"<<(*(thread_info *)argv).thread_name<<endl;
cout<<"main thread id:"<<(*(thread_info *)argv).thread_id<<endl;
pthread_exit((void*)pthread_self());//结束时传出线程自己的id
}
int main()
{
//下面结构体作为子线程参数
thread_info main_thread_info;
main_thread_info.thread_id=pthread_self();
main_thread_info.thread_name="Kyle";
//提前设置要创建线程的属性
pthread_attr_t attr;
int Ret=pthread_attr_init(&attr);
if(Ret)
{
cout<<"thread attrinit error!"<<endl;
}
Ret=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
if(Ret)
{
cout<<"thread set error!"<<endl;
}
//创建线程
pthread_t tid;
int Ret1= pthread_create(&tid,NULL,thread_func,(void *)&main_thread_info);
if(Ret1)
{
cout<<"thread error:Return value="<<Ret1<<endl;
return Ret1;
}
//回收线程(获取线程结束以后的状态量)
void* Ret_val;
int Ret2=pthread_join(tid,&Ret_val);
if(Ret2)
{
cout<<"thread join error"<<endl;
return Ret2;
}
cout<<"son thread id:"<<*((unsigned long *)Ret_val)<<endl;
return 0;
}
简单的多线程抢票程序
/********
//模拟多个线程"抢票"过程
//模拟在没有同步措施的情况下线程之间的抢占
//设定总票数ticket_num为全局变量
// 在每个线程中减1
********/
#include<pthread.h>
#include<iostream>
#include<string>
#include<stdio.h>
#include<unistd.h>
using namespace std;
int ticket_nums=10;//票数作为全局变量
void* thread_func(void *ticket)
{
//在,每个线程内部轮询抢票15次
for(int i=0;i<15;i++)
{
if((*(int *)ticket)>0)
{
sleep(1);//注意这里的一秒钟休眠 让其他线程有机可乘
(*(int *)ticket)--;
cout<<"In thread"<<pthread_self()<<" Buy one ticket...."<<endl;
}
else
{
cout<<"No Tickets Left...."<<endl;
}
}
pthread_exit(NULL);
}
int main()
{
//并发多个线程 模拟多个线程抢票的过程
int Ret;
pthread_t tid[4];
for(int i=0;i<4;i++)
{
//创建线程
Ret=pthread_create(&tid[i],NULL,thread_func,&ticket_nums);
if(Ret)
{
cout<<"creat thread error...."<<endl;
return Ret;
}
}
// 主线程休眠5秒 让子线程暂时脱离主线程的控制各自抢票
//如果此时主线程不休眠 马上往下执行会立刻关闭子线程 这样子线程就没有时间上演"抢票大战"
sleep(20);
for(int i=0;i<4;i++)
{
//销毁线程
Ret=pthread_join(tid[i],NULL);
if(Ret)
{
cout<<"destory thread error...."<<endl;
return Ret;
}
}
return 0;
}
理解多线程抢占的关键就是多个线程之间对与一个临界区没有保护 ,
导致一个线程在对临界区内存读写的时候 另外的线程也会过来读写 造成数据的干扰.
就好比 两个人甲 和乙去售票窗口买票 本来是一个一个人排队在窗口买
但是这个时候两个人谁也不让谁都挤到售票窗口抢着买票
买票要先问售票员还有几张票 再掏钱把票买走票数就减一了 (对应着临界区的读和写)
假设现在只有一张票了 甲先去询问售票员 售票员告诉它还有一张票 然后甲准备掏钱买这张票,
在甲准备掏钱买这张票的间隙 乙又问售票员还有几张票
由于这时甲还没有把这张票买走故此时售票员告诉乙还有一张票
于是乙也掏钱买这张票 这时就出现问题了 只剩下一张票了
甲乙同时买了 本着顾客就是上帝的原则 售票员也会给甲乙两个人一人一张票
但是这时票务系统就出错了 对应着临界区内存上的数据出错
于是我们要引入后续文章会讲到的线程同步措施