Linux系统编程 多线程

初识线程

线程的概念

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。
一切进程至少都有一个执行线程
线程在进程内部运行,本质是在进程地址空间内运行
在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

线程的优缺点

优点

创建一个新线程的代价要比创建一个新进程小得多。
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
线程占用的资源要比进程少很多。
能充分利用多处理器的可并行数量。
在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

缺点

1.频繁的线程切换会导致系统运行效率降低。
2.健壮性(鲁棒性)相对于进程来说低。因为进程之间是独立的,而线程不具有这样的特性。一个线程崩溃,随之而来的便是整个进程的崩溃。
3.多个线程在访问同一变量时,可能会造成二义性问题。

了解pid(轻量级线程号)与tgid(线程组id)

在这里插入图片描述
tgid为进程号,pid为线程号
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
Lwp为轻量级进程(线程),相比于多进程来说,轻量级进程可以与主进程共用一份地址空间,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符。

进程与线程概念解释

进程:是承担分配资源的基本实体,
在这里插入图片描述

线程:是调度的一个基本单位,线程是进程里面的一个执行流
在这里插入图片描述

同一所属组下多线程之间共享与独立的空间

在这里插入图片描述
在这里插入图片描述

进程与线程的对比

1.从性质上来说:进程是承担资源分配的基本单位,而线程是cpu调度的一个基本单位
2.从特性上来说,进程具有独立性,而线程不具有独立性。

多进程与多线程对比

在这里插入图片描述

线程控制

线程的创建

在这里插入图片描述

  1 #include<iostream>
  2 #include<unistd.h>
  3 #include<pthread.h>
  4 using namespace std;
  5 void *run(void *arg)
  6 {
  7     int *p=(int*)arg;
  8     while(1)
  9     {
 10         cout<<"i am thread1,~~~~~"<<*p<<endl;
 11         sleep(1);
 12     }
 13 }
 14 int main()
 15 {
 16     pthread_t tid;
 17     int i=10;
 18     int ret=pthread_create(&tid,NULL,run,(void*)&i);
 19     if(ret!=0)
 20     {
 21         return -1;
 22     }                                                                                                                                                                                 
 23     while(1)
 24     {
 25       cout<<"i am main thread"<<endl;
 26       sleep(1);
 27     }
 28     return 0;
 29 }

makefile

  1 mythread:test_creat.cpp
  2     g++ $^ -o $@ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f mythread                                                                                                                                                                    


在这里插入图片描述

线程入口的参数传递规则:
规则1:线程入口的参数不可以传递临时变量
规则2:传递的要是堆上开辟的空间,哪个线程用,哪个线程在用完后释放。
规则3:线程入口参数既可以传递内置类型,也可以传递自定义类型。

1 #include<iostream>                                                                                                                                                                    
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<pthread.h>
  5 using namespace std;
  6 class mythread
  7 {
  8     public:
  9         mythread(int key)
 10         {
 11             i=key;
 12         }
 13         ~mythread()
 14         {}
 15         void  start()
 16         {
 17            int ret= pthread_create(&tid,NULL,run,this);
 18            if(ret<0)
 19            {
 20                return ;
 21            }
 22         }
 23           static void*run(void *arg)
 24         {
 25                 mythread*pn=(mythread*)arg;
 26                 while(1)
 27                 {
 28 
 29                 cout<<"i am thread  "<<pn->i<<endl;
 30                 sleep(1);
 31                 }
 32         }
 33 
 34     private:
 35     pthread_t tid;
 36     int i;
 37 };
 38 int main()
 39 {
 40   mythread *rd=new mythread(5);
 41   rd->start();
 42   while(1)
 43   {
 44     cout<<" i am main thread"<<endl;
 45    sleep(1);
 46   }
 47   delete rd;
 48   return 0;
 49 }             

这个代码其实不是很好,因为没有考虑到让工作线程去释放。所以只供参考,不推荐写这样的代码
在这里插入图片描述

线程终止

常见线程退出的几种方式:
在这里插入图片描述

1: exit_thread.cpp ? ?                                                                                                                                                       ?? buffers 
  1 #include<iostream>                                                                                                                                                                    
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 using namespace std;
  6 struct student
  7 {
  8     void set(int num)
  9     {
 10         age=num;
 11     }
 12     int age;
 13 };
 14 void *run(void *arg)
 15 {
 16   student *pn=(student*)arg;
 17   int count=0;
 18   while(1)
 19   {
 20     if(count++==20)
 21     {
 22        // pthread_cancel(pthread_self());
 23       // pthread_exit(NULL);
 24         cout<<"我溜了,886"<<endl;
 25       return NULL;
 26     }
 27     cout<<"i am work pthread "<< pn->age<<endl;
 28   }
 29 }
 30 int main()
 31 {
 32 
 33     pthread_t tid;
 34     student *pre=new student;
 35     pre->set(20);
 36     pthread_create(&tid,NULL,run,pre);
 37     while(1)
 38     {
 39        cout<<"i am main thread"<<endl;
 40        sleep(1);
 41     }
 42     return 0;
 43 }                                                                                                                                                                                     


makefile

1 exit_thread:exit_thread.cpp
  2     g++ $^ -o $@ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f exit_thread   

在这里插入图片描述

线程等待

在这里插入图片描述

 1 #include<iostream>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 using namespace std;
  6 struct student
  7 {
  8     void set(int num)
  9     {
 10         age=num;
 11     }
 12     int age;
 13 };
 14 void *run(void *arg)
 15 {
 16   student *pn=(student*)arg;
 17   int count=0;
 18   while(1)
 19   {
 20       sleep(1);  //先让它睡1s,发现主线程仍旧不打印,证明为阻塞等                                                                                                                                                                     
 21     if(count++==20)
 22     {
 23        // pthread_cancel(pthread_self());
 24       // pthread_exit(NULL);
 25         cout<<"我溜了,886"<<endl;
 26       return NULL;
 27     }
 28     cout<<"i am work pthread "<< pn->age<<endl;
 29   }
 30 }
 31 int main()
 32 {
 33 
 34     pthread_t tid;
 35     student *pre=new student;
 36     pre->set(20);
 37     pthread_create(&tid,NULL,run,pre);
 38     pthread_join(tid,NULL);
 39     while(1)
  40     {
 41        cout<<"i am main thread"<<endl;
 42        sleep(1);
 43     }
 44     return 0;
 45 }         

在这里插入图片描述

线程分离

在这里插入图片描述
在这里插入图片描述

1 #include<iostream>
  2 #include<pthread.h>
  3 #include<unistd.h>
  4 #include<stdlib.h>
  5 using namespace std;
  6 struct student
  7 {
  8     void set(int num)
  9     {
 10         age=num;
 11     }
 12     int age;
 13 };
 14 void *run(void *arg)
 15 {
       //自身分离
       pthread_detach(pthread_self());
 16   student *pn=(student*)arg;
 17   int count=0;
 18   while(1)
 19   {
 20       sleep(1);  //先让它睡1s,发现主线程仍旧不打印,证明为阻塞等                                                                                                                                                                     
 21     if(count++==20)
 22     {
 23        // pthread_cancel(pthread_self());
 24       // pthread_exit(NULL);
 25         cout<<"我溜了,886"<<endl;
 26       return NULL;
 27     }
 28     cout<<"i am work pthread "<< pn->age<<endl;
 29   }
 30 }
 31 int main()
 32 {
 33 
 34     pthread_t tid;
 35     student *pre=new student;
 36     pre->set(20);
 37     pthread_create(&tid,NULL,run,pre);
 38     //pthread_join(tid,NULL);
        //在主线程中设置分离属性
        pthread_detach(tid);
 39     while(1)
  40     {
 41        cout<<"i am main thread"<<endl;
 42        sleep(1);
 43     }
 44     return 0;
 45 } 

线程安全

示例:
在这里插入图片描述

线程互斥:

互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界区起保护作用

为什么需要?

假设有两个线程同时进入临界区去访问临界资源时,假如两个线程都想对这个变量做一个++操作的话,此时因为他们同时拿到了这份资源,在执行完++操作后,本该两个执行流的++操作,其实最后只起到了一个执行流的效果。所以,当我们对临界区中的临界资源进行修改时,必须保证其原子性。而且必须保证拥有临界资源的执行流有且只有一个,这样可以避免运算结果的二义性。

1 #include<iostream>
    2 #include<stdio.h>
    3 #include<unistd.h>
    4 #include<pthread.h>
    5 using namespace std;
    6 const int num=4;
    7 int ticket=1000;
W>  8 void * getticket(void *arg)
    9 {
   10 
   11     while(1)
   12     {
   13         if(ticket>0)
   14         {
W> 15            printf("i am %p , i got a ticket %d\n",pthread_self(),ticket);                                                                                                           
   16            --ticket;
   17         }
   18         else
   19         {
   20             break;
   21         }
   22     }
W> 23 }
   24 int main()
   25 {
   26    pthread_t  arr[num];
   27     int i=0;
   28     for(;i<num;++i)
   29     {
   30        int ret= pthread_create(&arr[i],NULL,getticket,NULL);
   31        if(ret<0)
   32        {
   33            cout<<"create error"<<endl;
   34            return -1;
   35        }
   36     }
   37     i=0;
   38     for(;i<num;++i)
   39     {
   40         pthread_join(arr[i],NULL);
   41     }
   42     return 0;
   43 }        

两个不同的人拿到了同一张票,这便是线程不安全的情况
在这里插入图片描述

锁的本质

在这里插入图片描述
在这里插入图片描述

加锁的时机

在这里插入图片描述

加锁的接口

在这里插入图片描述
在这里插入图片描述
利用加锁解决掉上面拿到同一张票问题

1 #include<iostream>
    2 #include<stdio.h>
    3 #include<unistd.h>
    4 #include<pthread.h>
    5 using namespace std;
    6 const int num=4;
    //定义一个全局变量的锁
    7 pthread_mutex_t lock;
    8 int ticket=1000;
W>  9 void * getticket(void *arg)
   10 {
   11 
   12     while(1)
   13     {
                //上锁
   14         pthread_mutex_lock(&lock);
   15         if(ticket>0)
   16         {
W> 17            printf("i am %p , i got a   ticket%d\n",pthread_self(),ticket);
   18            --ticket;
   19         }
   20         else
   21         {
                   //确保在退出之前归还锁
   22             pthread_mutex_unlock(&lock);
   23             break;
   24         }
                  //取完一次后也要归还锁
   25             pthread_mutex_unlock(&lock);                                             
   26     }
   27 
W> 28 }
   29 int main()
   30 {
   31    pthread_t  arr[num];
   32     int i=0;
           //锁的初始化,动态初始化的,所以必须要释放该锁资源
   33     pthread_mutex_init(&lock,NULL);
   34     for(;i<num;++i)
   35     {
   36        int ret=pthread_create(&arr[i],NULL,getticket,NULL);
   37        if(ret<0)
   38        {
   39            cout<<"create error"<<endl;
   40            return -1;
   41        }
   42     }
   43     i=0;
   44     for(;i<num;++i)
   45     {
   46         pthread_join(arr[i],NULL);
   47     }
           //销毁该锁
   48     pthread_mutex_destroy(&lock);
   49     return 0;
   50 }              

加锁所带来的弊端

在这里插入图片描述

死锁

先构建出一个死锁情况

  1 #include<iostream>
    2 #include<pthread.h>
    3 #include<unistd.h>
    4 using namespace std;
    5 pthread_mutex_t lock1;
    6 pthread_mutex_t lock2;
    7 
W>  8 void *run1(void *arg)
    9 {
   10   pthread_mutex_lock(&lock1);
   11   sleep(3);
   12   pthread_mutex_lock(&lock2);
W> 13 }
W> 14 void *run2(void *arg)
   15 {
   16     pthread_mutex_lock(&lock2);
   17     sleep(3);
   18     pthread_mutex_lock(&lock1);
   19 
W> 20 }
   21 int main()
   22 {
   23    pthread_mutex_init(&lock1,NULL);
   24    pthread_mutex_init(&lock2,NULL);
   25    pthread_t mid1,mid2;
   26    pthread_create(&mid1,NULL,run1,NULL);
   27    pthread_create(&mid2,NULL,run2,NULL);
   28    pthread_join(mid1,NULL);
   29    pthread_join(mid2,NULL);
   30    while(1)
   31    {
   32      sleep(1);                                                                       
   33    }
   34    pthread_mutex_destroy(&lock1);
   35    pthread_mutex_destroy(&lock2);
   36 }

根据gdb理解死锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

死锁的概念

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态

线程产生的必要条件

互斥条件:一个资源每次只能被一个执行流使用
请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺
循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

避免死锁

1,资源一次性分配。
2,避免锁未释放的场景
3.加锁顺序一致。

线程同步

在这里插入图片描述

接口说明

初始化接口
在这里插入图片描述
等待接口
在这里插入图片描述
为什么第二个参数要传入锁?
在这里插入图片描述
在这里插入图片描述

唤醒接口
在这里插入图片描述
释放接口
int pthread_cond_destroy(pthread_cond_t *cond);
将初始化的条件变量释放。

基于同步实现生产者与消费者模型

在这里插入图片描述
在这里插入图片描述
优势:
在这里插入图片描述
阻塞队列
在这里插入图片描述
头文件:

1 #pragma once                                                                                                                                                                          
  2 #include<iostream>
  3 #include<pthread.h>
  4 #include<queue>
  5 #include<unistd.h>
  6 class task
  7 {
  8  public:
  9      int x,y;
 10  public:
 11      task()
 12      {}
 13      task(int _x,int _y):x(_x),y(_y)
 14      {}
 15      int run()
 16      {
 17          return x+y;
 18      }
 19      ~task()
 20      {
 21 
 22      }
 23      
 24 };
 25 class Block_queue
 26 {
 27 private:
 28     std::queue<task>t;//运行队列
 29     size_t cap;//容量
 30     pthread_mutex_t lock;//设置一把互斥锁
 31     pthread_cond_t consumer;//消费者在该条件下等
 32     pthread_cond_t productor;//生产者在该条件下等
 33 public:
 34      void  LockQueue()
 35      {
 36            pthread_mutex_lock(&lock);
 37      }
 38     void UnlockQueuue()
 39     {
 40           pthread_mutex_unlock(&lock);
 41     }
 42     bool isfull()
 43     {
 44         return t.size()==cap;
 45     }
 46     bool isempty()
 47     {
 48         return t.size()==0;
 49     }
 50     void WakeConsumer()
 51     {
 52         std::cout<<"Wake up consumer"<<std::endl;
 53         pthread_cond_signal(&consumer);
 54     }
 55     void WakeProductor()
 56     {
 57         std::cout<<"Wake up productor"<<std::endl; 
 58         pthread_cond_signal(&productor);
 59     }
 60     void ProductorWait()
 61     {
 62         std::cout<<"productor wait"<<std::endl;
 63         pthread_cond_wait(&productor,&lock);
 64     }
 65     void ConsumerWait()
 66     {
 67         std::cout<<"consumer wait"<<std::endl;
 68         pthread_cond_wait(&consumer,&lock);
 69     }
 70 public:
 71     Block_queue(size_t _cap)
 72     {                                                                                                                                                                                 
 73         cap=_cap;
 74         pthread_mutex_init(&lock,NULL);//初始化该锁
 75         pthread_cond_init(&consumer,NULL);
 76         pthread_cond_init(&productor,NULL);
 77     }
 78     void put(task t1)
 79     {
 80         LockQueue();
 81         while(isfull())
 82         {
 83             WakeConsumer();
 84             ProductorWait();
 85         }
 86         t.push(t1); 
 87         UnlockQueuue();
 88     }
 89     void get(task &t1)
 90     {
 91         LockQueue();
 92         while(isempty())
 93         {
 94             WakeProductor();
 95             ConsumerWait();
 96         }
 97         t1=t.front();
 98         t.pop();
 99         UnlockQueuue();
100     }
101     ~Block_queue()
102     {
103         pthread_mutex_destroy(&lock);
104         pthread_cond_destroy(&consumer);                                                                                                                                              
105         pthread_cond_destroy(&productor);
106     }
107 };



main函数

 1 #include"c_p.hpp"
  2 #include<stdlib.h>
  3 using namespace std;
  4 pthread_mutex_t lock1,lock2;//lcok1为生产者锁,lock2为消费者锁
  5 void* pro_run(void*arg)
  6 {
  7    Block_queue *rq=(Block_queue*)arg;
  8    while(1)
  9    {
 10        //确保生产者先生产满
 11        sleep(1);
 12        //加的是生产者的锁
 13        pthread_mutex_lock(&lock1);
 14        int x=rand()%20+1;
 15        int y=rand()%30+1;
 16        task t(x,y);
 17        rq->put(t);
 18        cout<<"productor send task"<<x<<"+"<<y<<" = ?"<<endl;
 19        pthread_mutex_unlock(&lock1);
 20        sleep(1);
 21    }
 22 }
 23 void *cu_run(void *arg)
 24 {
 25   Block_queue *rq=(Block_queue*)arg;
 26   while(1)
 27   {
 28      pthread_mutex_lock(&lock2);
 29      task t;
 30      rq->get(t);                                                                                                                                                                      
 31      cout<<"consumer get task"<<t.x<<"+"<<t.y<<"= "<<t.run()<<endl;
 32      pthread_mutex_unlock(&lock2);
 33   }
 34 }
 35 int main()
 36 {
 37     pthread_t p1,p2,p3;//3个生产者
 38     pthread_t c1,c2,c3;//3个消费者
 39     //开辟一块新空间,这个空间中最多容纳5个元素
 40     Block_queue *rq=new Block_queue(5);
 41     //初始化锁
 42     pthread_mutex_init(&lock1,NULL);
 43     pthread_mutex_init(&lock2,NULL);
 44     //3个生产者线程
 45     pthread_create(&p1,NULL,pro_run,(void*)rq);
 46     pthread_create(&p2,NULL,pro_run,(void*)rq);
 47     pthread_create(&p3,NULL,pro_run,(void*)rq);
 48     //3个消费者线程
 49     pthread_create(&c1,NULL,cu_run,(void*)rq);
 50     pthread_create(&c2,NULL,cu_run,(void*)rq);
 51     pthread_create(&c3,NULL,cu_run,(void*)rq);
 52     pthread_join(p1,NULL);
 53     pthread_join(p2,NULL);
 54     pthread_join(p3,NULL);
 55     pthread_join(c1,NULL);
 56     pthread_join(c2,NULL);
 57     pthread_join(c3,NULL);
 58     delete rq;
 59     return 0;
 60 }      

makefile:

  1 main:mymain.cpp
  2     g++ $^ -o $@ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f main  

运行结果
在这里插入图片描述

POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

接口说明

初始化接口

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值

信号量销毁

int sem_destroy(sem_t *sem);

等待信号量
p操作

功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem);

归还信号量
v操作

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1int sem_post(sem_t *sem)

基于环形队列实现

 1 #pragma once                                                                                                                                                                        
    2 #include<iostream>
    3 #include<unistd.h>
    4 #include<semaphore.h>
    5 #include<vector>
    6 #define NUM 10
    7 class Ring_Queue
    8 {
    9     private:
   10        int cap;
   11        std::vector<int>p;
   12        int c_index;//生产者下标
   13        int p_index;//消费者下标
             sem_t lock;//加锁,
   14        sem_t sem_blank;//对应空格字
   15        sem_t sem_data;//格子中的数据
   16     public:
   17        void P(sem_t &t)
   18        {
   19            sem_wait(&t);
   20        }
   21        void V(sem_t &t)
   22        {
   23            sem_post(&t);
   24        }
   25     public:
W> 26     Ring_Queue(int _cap=NUM):p(_cap),cap(_cap)
   27     {
   28       sem_init(&sem_blank,0,NUM);//初始化生产者。默认情况下有 NUM个空各自
   29       sem_init(&sem_data,0,0);//初始化消费者,默认情况下0个
            sem_init(&lock,0,1);//默认0/1起到加锁作用,读写只能一个
   30       c_index=0;
   31       p_index=0;
   32     }
   33     void Put(const int &t)
   34     {
              
   35         P(sem_blank);
              sem_wait(lock);
   36         p[p_index]=t;
   37         p_index++;
   38         p_index%=NUM;
   39         V(sem_data);
              sem_post(lock);
 40     }
   41     void Get(int &t)
   42     {
              
   43         P(sem_data);//必须判断有空间才可以拿锁,要不然拿锁后直接被放在等待队列中,此时假如说读线程想要进行读的话,此时它申请不到锁,就会造成死锁现象
              sem_wait(lock);
   44         t=p[c_index];
   45         c_index++;
   46         c_index%=NUM;
   47         V(sem_blank);
              sem_post(lock);
   48     }
   49     ~Ring_Queue()
   50     {
   51         sem_destroy(&sem_blank);
   52         sem_destroy(&sem_data);
              sem_destroy(lock);
   53         c_index=0;
   54         p_index=0;
   55     }
   56 
   57 };        

main

 1 #include"ring.hpp"
  2 void *productor(void *arg)
  3 {
  4   Ring_Queue *rq=(Ring_Queue*)arg;
  5       int count=0;
  6   while(1)
  7   {
  8       rq->Put(count);
  9       count++;
 10       if(count>10)
 11       {
 12           count=0;                                                                                                                                                                    
 13       }
 14       std:: cout<<"productor over"<<std::endl;
 15   }
 16 }
 17 void * consumer(void *arg)
 18 {
 19    Ring_Queue *rq=(Ring_Queue*)arg;
 20    while(1)
 21    {
 22        sleep(1);
 23        int data;
 24        rq->Get(data);
 25        std::cout<<"consumer get a data "<<data<<std::endl;
 26    }
 27 
 28 }
 29 int main()
 30 {
 31     Ring_Queue *rq=new Ring_Queue();
 32     pthread_t c,p;
 33     pthread_create(&c,NULL,consumer,rq);
 34     pthread_create(&p,NULL,productor,rq);
 35     pthread_join(c,NULL);
 36     pthread_join(p,NULL);
 37     return 0;
 38 }
~

makefile

1 main:main.cpp
  2     g++ $^ -o $@ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f main   

线程池

在这里插入图片描述
在这里插入图片描述

线程池代码

.hpp文件

 1 #pragma once                                                                                                                                                                          
  2 #include<iostream>
  3 #include<pthread.h>
  4 #include<queue>
  5 #include<unistd.h>
  6 #include<stdlib.h>
  7 #include<cmath>
  8 #define NUM 6
  9 class Task
 10 {
 11   private:
 12       int base;
 13   public:
 14       Task(){}
 15       Task(int _base):base(_base)
 16       {}
 17       void run()
 18       {
 19           std::cout<<"thread is ["<<pthread_self()<<"]"<<"process this task"<< "pow("<<base<<",2)is"<<pow(base,2)<<std::endl;
 20       }
 21       ~Task(){}
 22 };
 23 class Thread_pool
 24 {
 25     private:
 26       std::queue<Task*>v;
 27       int max_num;
 28       pthread_mutex_t lock;
 29       pthread_cond_t cond;
 30       bool quit;
 31     public:
 32       void thread_wait()
 33       {
 34           pthread_cond_wait(&cond,&lock);
 35       }
 36       void LockQueue()
 37       {
 38         pthread_mutex_lock(&lock);
 39       }
 40       void UnlockQueue()
 41       {
 42         pthread_mutex_unlock(&lock);
 43       }
 44      static void*RUN(void *arg)
 45      {
 46         Thread_pool *this_p=(Thread_pool*)arg;
 47         while(true)
 48         {
 49             this_p->LockQueue();
 50             while(this_p->Is_Empty())
 51             {
 52               this_p-> thread_wait();
 53             }
 54             Task t;
 55             this_p->Get(t);
 56             this_p->UnlockQueue();
 57             t.run();
 58         }
 59      }
 60      bool Is_Empty()
 61      {
 62           return v.size()==0;
 63      }
 64      void Thread_Wakeup()
 65      {
 66          pthread_cond_signal(&cond);
 67      }
 68      void Threads_Wake()
 69      {
 70          pthread_cond_broadcast(&cond);
 71      }
 72     public:
 73       Thread_pool(int _max=NUM):max_num(_max),quit(false)                                                                                                                             
 74       {}
 75       void Thread_init()
 76       {
 77           pthread_mutex_init(&lock,NULL);
 78           pthread_cond_init(&cond,NULL);
  79           pthread_t t;
 80           int i=0;
 81           for(;i<max_num;++i)
 82           {
 83                pthread_create(&t,NULL,RUN,this);
 84           }
 85       }
 86       void Put( Task &in)
 87       {
 88           LockQueue();
 89           v.push(&in);
 90           UnlockQueue();
 91           Thread_Wakeup();
 92       }
 93      void Get(Task &t)
 94      {
 95         Task*p=v.front();
 96         t=*p;
 97         v.pop();
 98      }
 99      void Thread_Quit()
100      {
101          if(!Is_Empty())
102          {
103              std::cout<<"queue is not empty,can not quit"<<std::endl;
104              return;
105          }
106          else
107          {
108              quit=true;
109              Threads_Wake();
110          }
111      }
112      ~Thread_pool()                                                                                                                                                                   
113      {
114          pthread_mutex_destroy(&lock);
115          pthread_cond_destroy(&cond);
116      }
117 

118 };  



.cpp文件

  1 #include"thread_pool.hpp"
  2 int main()
  3 {
  4     Thread_pool *rq=new Thread_pool();                                                                                                                                                
  5     rq->Thread_init();
  6 
  7     while(true)
  8     {
  9          int x=rand()%10+1;
 10          Task t(x);
 11          rq->Put(t);
 12          sleep(1);
 13     }
 14     return 0;
 15 }
~
~

makefile

  1 thread_pool:main.cpp
  2     g++ $^ -o $@ -lpthread
  3 .PHONY:clean
  4 clean:
  5     rm -f thread_pool                                                                                                                                                                 

在这里插入图片描述
在这里插入图片描述

线程池中的惊群问题

解释:简单地说:就是扔一块食物,所有鸽子来抢,但最终只一个鸽子抢到了食物。
对应多线程就是:假如此时来了一个任务的话,所有线程池中休眠的线程均被唤醒。在多进程/多线程等待同一资源时,也会出现惊群。即当某一资源可用时,多个进程/线程会惊醒,竞争资源。这就是操作系统中的惊群。
惊群效应所产生的坏处:
1.惊醒所有进程/线程,导致n-1个线程做了无效的调度,上下文切换,cpu瞬时增高(单个任务)
2.多个进程/线程争抢资源,所以涉及到同步问题,需对资源进行加锁保护,加解锁加大系统CPU开销。

正常的用法:

所有线程共用一个锁,共用一个条件变量
当pthread_cond_signal通知时,就可能会出现惊群
解决惊群的方法:

所有线程共用一个锁,每个线程有自已的条件变量(在初始化传入的第二个参数)
pthread_cond_signal通知时,定向通知某个线程的条件变量,不会出现惊群。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Linux网络编程中,使用多线程进行收发操作可以提高网络应用程序的并发性能。下面是一个简单的示例代码,说明如何使用多线程进行收发操作。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #define MAX_CLIENTS 10 void *handle_client(void *arg) { int client_fd = *(int *)arg; char buffer[1024]; memset(buffer, 0, sizeof(buffer)); // 接收客户端数据 ssize_t recv_len = recv(client_fd, buffer, sizeof(buffer), 0); if (recv_len > 0) { printf("Received message from client: %s\n", buffer); } // 发送响应给客户端 char response[] = "Hello from server!"; ssize_t send_len = send(client_fd, response, sizeof(response), 0); if (send_len == -1) { perror("send"); } close(client_fd); pthread_exit(NULL); } int main() { int server_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; socklen_t client_addr_len = sizeof(client_addr); pthread_t threads[MAX_CLIENTS]; // 创建套接字 server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { perror("socket"); exit(1); } // 设置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(8080); // 绑定套接字到服务器地址 if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); exit(1); } // 监听连接 if (listen(server_fd, MAX_CLIENTS) == -1) { perror("listen"); exit(1); } printf("Server started. Listening on port 8080...\n"); // 接受并处理客户端请求 int i = 0; while (1) { int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len); if (client_fd == -1) { perror("accept"); continue; } // 创建线程处理客户端请求 if (pthread_create(&threads[i], NULL, handle_client, &client_fd) != 0) { perror("pthread_create"); continue; } // 分离线程 pthread_detach(threads[i]); i++; if (i >= MAX_CLIENTS) { break; } } close(server_fd); return 0; } ``` 在上面的示例代码中,首先创建了一个服务器套接字,然后绑定到指定的IP地址和端口号。接着,使用`listen`函数监听来自客户端的连接请求。 在主循环中,通过调用`accept`函数接受客户端连接,并且为每个客户端连接创建一个新的线程来处理收发操作。`handle_client`函数是在线程中执行的任务,负责接收客户端数据并发送响应给客户端。 需要注意的是,在多线程编程中,要确保网络资源的正确管理和共享,避免竞争条件和死锁等问题的出现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值