Linux系统编程 多线程

Posted 蚍蜉撼树谈何易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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以上是关于Linux系统编程 多线程的主要内容,如果未能解决你的问题,请参考以下文章

linux下多线程编程

什么是Java多线程编程?

多线程编程之Linux环境下的多线程

linux多线程服务端编程 看啥书

Linux多线程编程详解 [By: HarryAlex]

Linux系统编程 多线程