c/c++语言开发共享C++学习之线程的概念、创建以及使用实例

线程的概念 线程是执行的基本单位。每个进程至少有一个线程。 一个进程里可以存在多个线程,多个线程共享一个进程的资源。 共享的资源包括数据段、代码段和堆中的内容。 而线程私有的部分,在栈帧。每个线程拥

线程的概念

线程是执行的基本单位。每个进程至少有一个线程。

一个进程里可以存在多个线程,多个线程共享一个进程的资源。

共享的资源包括数据段、代码段和堆中的内容。

而线程私有的部分,在栈帧。每个线程拥有自己的栈帧,栈帧内的资源是线程私有的。

线程有自己的tid和自己的tcb。

创建线程

操作向用户提供了创建线程的库函数:

  compile and link with -pthread  #include   int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);  功能:创建一个新的线程  参数:  thread:用于存储线程的tid  attr:null,不涉及线程的属性  start_routine:线程执行的函数  arg:指定了传递给线程执行函数的唯一参数  返回值:  success:0  error:返回错误码,thread参数的内容不确定    获取线程自己的tid  pthread_t pthread_self(void);  compile and link with -pthread.

线程的终止,汇合,分离

1、在线程函数中的return,会终止线程

2、在线程执行过程中不要使用exit(3),会结束当前进程。

3、可以使用pthread_exit(3),结束线程自己

4、pthread_cancel,取消一个线程

  #include   void pthread_exit(void *retval);  compile and link with -pthread.  功能:终止当前线程  参数:  retval:通过retval返回一个值,给进程中的另外一个线程使用,调用用了pthread_join(3)  返回值:  这个函数不返回给调用者  ----------------------------------------------------------------------------  int pthread_cancel(pthread_t thread);  功能:给一个线程发送取消请求  参数:  thread:指定了接受请求的线程的id  返回值:  success:0  error:not0     注意:如果线程已经被取消,另外调用pthread_join的线程,获取到的是pthread_canceled

一个线程执行结束,如果没有其他线程为它回收资源,那么这些资源就会一直被占用,直到进程结束,由系统回收。

通常一个服务程序运行起来是不会停止的,大量的僵尸线程,会导致内存泄漏。

操作系统提供了两种回收机制:

1、由其他线程来为消亡的线程回收资源。

其他线程通过一个库函数,等待需要被回收的线程消亡。如果其他线程在需要被回收的线程消亡前执行到等待函数,就会进入阻塞等待状态,直到需要被回收的线程消亡,然后回收资源,并接触阻塞,继续向下执行。这种方式叫做线程的汇合。

  int pthread_join(pthread_t thread, void **retval);  功能:汇合一个终止的线程  参数:  thread:指定了要汇合的线程的id  retcal:指定了存放线程退出状态码的地址  返回值:  success:0  error:返回错误编号

2、由操作系统为消亡的线程回收资源。

其他线程通过一个库函数,告诉操作系统需要回收资源的线程将由系统执行回收动作。需要回收资源的线程在结束之后,就会被系统回收。且其他线程不会产生阻塞状态。

  int pthread_detach(pthread_t thread);  功能:分离一个线程  参数:  thread:指定了要分离的线程的id  返回值:  success:0  error:返回一个错误编号

线程的同步

线程的同部分为两种。

第一种:由于争抢资源而被迫同步

线程是异步的,且都是可以访问进程的公共资源,数据段、代码段、堆。如果两个线程同时访问一个全局变量,会出现不稳定的结果。为了保证数据的有效性,需要在线程准备访问公共资源之前占有和这个全局变量对应(这种讲法是不严谨的,一个资源和一把mutex锁的对应,是由主观决定的,并没有实际的联系)的mutex锁。对于mutex锁的操作如下:

  #include   pthread_mutex_t mutex = pthread_mutex_initializer;//静态初始化一个mutex锁  int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);  功能:  参数:  mutex:指定要初始化的mutex  attr:null  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_mutex_destroy(pthread_mutex_t *mutex);  功能:销毁mutex锁  参数:  mutex:指定要销毁的mutex锁  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_mutex_lock(pthread_mutex_t *mutex);  功能:加锁,如果锁被其他线程占用,阻塞等待。加锁成功,当前线程成为这把锁的拥有者  参数:  mutex:指定要使用的锁  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_mutex_trylock(pthread_mutex_t *mutex);  功能:和上个函数一样,但是在锁被其他线程占用的时候,立即返回,返回错误  参数:  mutex:指定要使用的锁  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_mutex_unlock(pthread_mutex_t *mutex);  功能:释放一把锁。在其他阻塞等待获取这把锁的线程,从中选择其一获取这把锁  参数:  mutex:指定要释放的锁  返回值:  success:0  error:错误码  

第二种:线程间达成条件而同步

为了达到目的,通过条件变量来迫使线程同步。

条件变量和上述的mutex锁一样,是一种类型,由系统提供。

两个线程,线程a和b,线程a执行的时候,需要一个条件,由于条件不满足线程a阻塞等待条件成立,线程b执行,使得a等待的条件成立。这个条件就是条件变量类型的具体的变量。

条件变量的操作如下:

  #include   pthread_cond_t cond = pthread_cond_initializer;  静态初始化一个条件变量  int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);  功能:初始化一个条件变量,  参数:  cond:指定要初始化的条件变量  attr:null,默认属性  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_cond_destroy(pthread_cond_t *cond);  功能:销毁条件变量  参数:  cond:指定要销毁的条件变量  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);  功能:等待条件的满足  参数:  cond:指定条件变量,等待为真的条件变量  mutex:指定一个mutex锁  返回值:  success:0  error:错误码    以下三步,是这个函数的功能,这三步时原子性的,无法中断。  1、释放mutex锁  2、阻塞等待条件变量为真  3、当条件变量满足的时候,获取锁。  -------------------------------------------------------------------------------------------------    #include   int pthread_cond_signal(pthread_cond_t *cond);  功能:从等待条件变量变为真的线程中挑一个,接触它的阻塞。  参数:  cond:指定等待条件变量  返回值:  success:0  error:错误码  -------------------------------------------------------------------------------------------------    int pthread_cond_broadcast(pthread_cond_t *cond);  signal是发送给一个等待线程,而broadcast是发送给所有的等待线程,无论哪个线程可以接触非阻塞状态获得mutex锁都一样。

下面通过一个综合联系来演示上述函数。

这个例子种,由2个生产者和1个消费者,生产者生产数据并将数据加入到链表,消费者移除链表中的数据,一次一个。如果消费者发现链表为空,就等待生产者生产。如果生产者发现链表中数据大于等于10就停止生产,直到消费者将链表清空。

  #include   #include   #include   #include   #include   //定义生产内容的结构体  typedef struct node{      int data;      struct node *next;  }node_t;  //使用链表的数据结构存储生产的物品  typedef struct node *list_t;  //将头节点初始化为空,防止野指针  list_t head = null;  //静态声明一把mutex锁  pthread_mutex_t mutex_head = pthread_mutex_initializer;  //静态声明一个条件变量  pthread_cond_t cond_consume = pthread_cond_initializer;  //静态声明一个条件变量  pthread_cond_t cond_product = pthread_cond_initializer;  //生产程序  void *product(void *arg){      //一直生产      while(1){          //即将访问全局变量,上锁          pthread_mutex_lock(&mutex_head);          //清点一下链表中的个数          int count = 0;          for(node_t *temp = head;temp;temp = temp -> next)              count++;          //如果生产过量就停止生产          if(count >= 10)              pthread_cond_wait(&cond_product,&mutex_head);          //创建一个节点          node_t *new = (node_t *)malloc(sizeof(node_t));          //初始化节点内容          new -> data = rand()%1000 + 1;          new -> next = null;          //打印节点内容          printf("新产生:%dn",new -> data);          //将新产生的节点插入到链表          new -> next = head;          head = new;          //对全局变量的操作完毕,解锁          pthread_mutex_unlock(&mutex_head);          //通知正在等待的消费者,生产完毕          pthread_cond_signal(&cond_consume);          //随机等待          sleep(rand()%5 + 1);      }  }  //消费者执行程序  void *consume(void *arg){      //一直消费      while(1){          //即将访问全局变量,上锁          pthread_mutex_lock(&mutex_head);          //如果链表空,阻塞等待生产者          while(!head){              //告诉一个生产者,库存消费完了              pthread_cond_signal(&cond_product);              //将锁释放,让生产者占有这把锁去生产,阻塞等待满足条件变量,满足后在占有锁              pthread_cond_wait(&cond_consume,&mutex_head);          }          //消费          node_t *tmp = head;          head = head -> next;          //消费者查看库存量          printf("库存量:");          for(node_t *temp = head;temp;temp = temp -> next)              printf("%d ",temp -> data);          printf("n");          //对全局变量的访问结束,释放锁          pthread_mutex_unlock(&mutex_head);          //消费结果          printf("已消费:%dn",tmp -> data);          //释放空间          free(tmp);          //防止出现野指针          tmp = null;          //随即睡眠          sleep(rand()%5 + 1);      }  }  int main(void){      pthread_t pidone = 0,pidtwo = 0,cid = 0;      //创建两个进程,一个用于生产者和消费者      srand(time(null));      pthread_create(&pidone,null,product,null);      pthread_create(&pidtwo,null,product,null);      pthread_create(&cid,null,consume,null);      //阻塞等待线程结束      pthread_join(pidone,null);      pthread_join(pidtwo,null);      pthread_join(cid,null);      //销毁锁和条件变量      pthread_mutex_destroy(&mutex_head);      pthread_cond_destroy(&cond_consume);  }

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/607361.html

(0)
上一篇 2021年5月14日
下一篇 2021年5月14日

精彩推荐