(深入浅出,推荐!)多线程零基础到实战系列2-----写一个简单的多线程并发的抢票程序

引入:

在这个系列的第一篇博文中介绍了线程的基本概念
这篇文章就立刻投入实战,
讲一讲怎么样写一个简单的多线程并发的抢票程序
也就是说 用多个线程对临界区数据的抢占
来模拟多个人不排队在售票窗口抢票的场景

首先我们会介绍和线程相关的api
然后用一个小例子介绍这些api的使用方法
最后就是讲解这个"抢票程序"

目录

基本API

需要包括的头文件

线程创建函数

主线程回收子线程函数

子线程自我结束函数

获得线程id

设置线程属性

基本实例


基本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;

 }

理解多线程抢占的关键就是多个线程之间对与一个临界区没有保护 ,
导致一个线程在对临界区内存读写的时候 另外的线程也会过来读写 造成数据的干扰.
就好比 两个人甲 和乙去售票窗口买票 本来是一个一个人排队在窗口买
但是这个时候两个人谁也不让谁都挤到售票窗口抢着买票  
买票要先问售票员还有几张票 再掏钱把票买走票数就减一了 (对应着临界区的读和写)
假设现在只有一张票了 甲先去询问售票员 售票员告诉它还有一张票 然后甲准备掏钱买这张票,
在甲准备掏钱买这张票的间隙 乙又问售票员还有几张票
由于这时甲还没有把这张票买走故此时售票员告诉乙还有一张票
于是乙也掏钱买这张票 这时就出现问题了 只剩下一张票了
甲乙同时买了 本着顾客就是上帝的原则   售票员也会给甲乙两个人一人一张票
但是这时票务系统就出错了 对应着临界区内存上的数据出错 
于是我们要引入后续文章会讲到的线程同步措施

发布了61 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/vjhghjghj/article/details/100569924