线程(学习笔记)

一、线程的概念

说起线程,我们先说说进程。

1.1、什么是进程

        每个进程都拥有自己的数据段代码段堆栈段,这就造成进程在进行创建(即:4G的虚拟内存)、切换、撤销操作时,需要较大的系统开销。为了减少系统开销,从进程中演化出了线程。

1.2、什么是线程

        线程是进程中的独立控制流,由环境(包括寄存器组和程序计数器)和一系列的执行指令组成。线程存在于进程中,共享进程的资源。每个进程有一个地址空间和一个控制线程。

 二、线程总结

1)一般把线程称之为轻量级的进程
2)一个进程可以创建多个线程,多个线程共享一个进程的资源
3)每一个进程创建的时候系统会给其4G虚拟内存,3G用户空间是私有的,所以进程切换时,用户空间也会切换,所以会增加系统开销,而一个进程中的多个线程共享一个进程的资源,所以线程切换时不用切换这些资源,效率会更高。
4)线程的调度机制跟进程是一样的,多个线程来回切换运行。

三、多线程

3.1、使用多线程的目的主要有以下几点:

    (1) 多任务程序的设计
一个程序可能要处理不同应用,要处理多种任务,如果开发不同的进程来处理,系统开销很大,数据共享,程序结构都不方便,这时可使用多线程编程方法。
    (2)并发程序设计
一个任务可能分成不同的步骤去完成,这些不同的步骤之间可能是松散耦合,可能通过线程的互斥,同步并发完成。这样可以为不同的任务步骤建立线程。 
    (3)网络程序设计
为提高网络的利用效率,我们可能使用多线程,对每个连接用一个线程去处理。
    (4)数据共享
同一个进程中的不同线程共享进程的数据空间,方便不同线程间的数据共享。在多CPU系统中,实现真正的并行。

四、线程的基本操作

4.1、线程的创建

  头文件:

               #include <pthread.h>
 函数:

        int pthread_create (pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
 功能:创建一个新的子线程
 参数:
           thread:当前创建的线程id
           attr:线程的属性,设置为NULL表示以默认的属性创建
           start_routine:线程处理函数,如果当前函数执行完毕,则子线程也执行完毕
           arg: 给线程处理函数传参用的
 返回值:
           成功:0
           失败:非0

 唯一难点的是  线程处理函数,也是此创建子线程运行的区域。(不管用不用都需要定义出来)

分析线程处理函数   void *(*start_routine) (void *)

         返回类型void *,子线程名可以随意取,函数传参也是void *

void * pthread_fun1(void *arg)
{
  
    
     printf("子线程工作区域\n");
 
 
   
   }

4.2、创建一个子线程 (注意;当进程结束,线程也会结束,线程依赖于进程)

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>

void * pthread_fun1(void *arg)
{
     printf("子线程工作区域\n");
    
    }

int main()
{ 
    int ret;
    pthread_t pthread;
    int data=10;
    printf("主控线程工作区域\n");
    
    //ret=pthread_create(&pthread,NULL,pthread_fun1,NULL);//不传参,填NULL
    ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
   
      if( ret != 0)
     {           
         perror("fail to pthread_create");
         exit(1); 
        }
  while(1); //为什么需要用while,如果不用while,当主线程运行完,即进程运行结束,子线程就会释放
            //也就是说,当main下面函数都执行完了,进程就结束了,不管子线程是否运行结束
   
 }

注意:编译需要加 -lpthread        即:gcc 目标文件  -lpthread -o 执行文件(可随意取) 

运行结果

 主控线程工作区域

 子线程工作区域

...

五、探究多个线程与主控线程执行顺序

5.1、当主控线程(即main函数)执行创建子线程时,是否接着执行后面语句。

答案:会接着执行

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>

void * pthread_fun1(void *arg)
{
     printf("子线程工作区域\n");
    
    }

int main()
{ 
    int ret;
    pthread_t pthread;
    int data=10;
    printf("主线程工作区域\n");
    
    
      ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
   
      if( ret != 0)
     {           
         perror("fail to pthread_create");
         exit(1); 
        }
       
       printf("hello \n");

  while(1);

     return 0;
 }

运行结果

主控线程工作区域

 hello

 子线程工作区域

...

问题又来了,一定是先运行hello,接着再运行  子线程??

答案:不一定,它们先后顺序,看它们谁先争夺CPU,谁先运行。

让hello 此语句睡眠一秒,代码如下所示

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
     printf("子线程工作区域\n");
    
    }

int main()
{ 
    int ret;
    pthread_t pthread;
    int data=10;
    printf("主线程工作区域\n");
    
    
      ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
   
      if( ret != 0)
     {           
         perror("fail to pthread_create");
         exit(1); 
        }
       sleep(1);
       printf("hello \n");

  while(1);

     return 0;
 }

运行结果

主控线程工作区域

子线程工作区域 

hello

5.2、探究多个子线程运行顺序

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
     printf("子线程1工作区域\n");
    
    }
void * pthread_fun2(void *arg)
{
     printf("子线程2工作区域\n");
    
    }

int main()
{ 
    int ret1;
    int ret2;
    pthread_t pthread1;
    pthread_t pthread2;
    int data=10;

    printf("主线程工作区域\n");
    
    
   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
    if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create2");
         exit(1); 
        }
 
       printf("hello1 \n");
       printf("hello2 \n");
  while(1);

     return 0;
 }

运行结果   顺序有多种(我只写一种)

 主线程工作区域
子线程1工作区域
hello1 
hello2 
子线程2工作区域

总结,不管有多少个子线程,它们的顺序,由它们争夺CPU决定,谁先得到CPU 谁先运行

六、探究线程共享数据

6.1,全局变量,子线程共享

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>


int q =120;

void * pthread_fun1(void *arg)
{
     printf("子线程1工作区域\n");
     printf("q=%d\n",q);
    // int a=*(int *)arg;
     //printf("a= %d\n",a);
    
    }
void * pthread_fun2(void *arg)
{
     printf("子线程2工作区域\n");
     printf("q=%d\n",q);
   //  int a=*(int *)arg;
   //  printf("a= %d\n",a);
    
    }

int main()
{ 
    int ret1;
    int ret2;
    pthread_t pthread1;
    pthread_t pthread2;
    int data=100;

    printf("主线程工作区域\n");
    
    
   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
    if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create2");
         exit(1); 
        }
 
  while(1);

     return 0;
 }

运行结果

主线程工作区域
子线程1工作区域
q=120
子线程2工作区域
q=120 

6.2、子线程可以改变全局变量

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>


int q =120;

void * pthread_fun1(void *arg)
{
     printf("子线程1工作区域\n");
     printf("q=%d\n",q);
     q = q+10;
     printf("子线程1 q = %d\n",q);
    
    }
void * pthread_fun2(void *arg)
{
     sleep(1);
     printf("子线程2工作区域\n");
     printf("子线程2 q = %d\n",q);
 
    
    }

int main()
{ 
    int ret1;
    int ret2;
    pthread_t pthread1;
    pthread_t pthread2;
    int data=100;

    printf("主线程工作区域\n");
	
	
    
    
   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
    if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create2");
         exit(1); 
        }
		sleep(2);
		printf("主线程 %d\n",q);
 
  while(1);

     return 0;
 }

运行结果

主线程工作区域
子线程1工作区域
q=120
子线程1 q = 130
子线程2工作区域
子线程2 q = 130
主线程 130

注意:主线程打印q时,需要提前创建好子线程,不然,会从main函数顺序执行。 

七、线程传参

7.1、最基本线程传参

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
     printf("子线程1工作区域\n");
     
     int a=*(int *)arg;
     printf("a= %d\n",a);
    
    }
void * pthread_fun2(void *arg)
{
     printf("子线程2工作区域\n");
    
     int a=*(int *)arg;
     printf("a= %d\n",a);
    
    }

int main()
{ 
    int ret1;
    int ret2;
    pthread_t pthread1;
    pthread_t pthread2;
    int data=100;

    printf("主线程工作区域\n");
    
    
   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
    if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create2");
         exit(1); 
        }
 
  while(1);

     return 0;
 }

运行结果

 主线程工作区域
子线程2工作区域
a= 100
子线程1工作区域
a= 100

7.2、子线程改变传参的值

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>



void * pthread_fun1(void *arg)
{
     printf("子线程1工作区域\n");
     
     int a=*(int *)arg;
     printf("a= %d\n",a);
	 a++;
     printf("a= %d\n",a);

       *(int *)arg=1200;
    }
void * pthread_fun2(void *arg)
{
     printf("子线程2工作区域\n");
     
	 sleep(1);
     int b=*(int *)arg;
     printf("b= %d\n",b);
    
    }

int main()
{ 
    int ret1;
    int ret2;
    pthread_t pthread1;
    pthread_t pthread2;
    int data=100;

    printf("主线程工作区域\n");
    
    
   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
    if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create2");
         exit(1); 
        }
 
  while(1);

     return 0;
 }

运行结果

 主线程工作区域
子线程1工作区域
a= 100
a= 101
子线程2工作区域
b= 1200

八、pthread——join等待子线程 

头文件:

               #include <pthread.h>
函数:

               int pthread_join(pthread_t thread, void **retval);
功能:

               1)阻塞等待一个子线程的退出,
               2)可以接收到某一个子线程调用pthread_exit时设置的退出状态值
参数:
               thread:指定线程的id
                retval:保存子线程的退出状态值,如果不接受则设置为NULL
返回值:
             成功:0
             失败:非0

扩展;传参

 定义  int a            -——main——f(a)         -——子函数——f(int b)  —取出来—b

 定义   int *a          -——main——f(&a)       -——子函数——f(int **b)  —取出来—*b

九、pthread_create 搭配函数 pthread_join等待子线程退出

9.1、pthread_join不接收返回值(代码)

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>



void * pthread_fun1(void *arg)
{
      static int re_val =100;
     printf("子线程1工作区域\n");
     sleep(3);
	 printf("end \n");
    return (void *)&re_val;
    }

int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;

    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     if(pthread_join(pthread1,NULL)!=0)
      {
		  perror("fail to pthread_join");
		  exit(1);
		  }
    
     return 0;
 }

运行结果

主线程工作区域
子线程1工作区域
end 

9.2、pthread——join 接收返回值(代码)

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
      static int re_val =100;
     printf("子线程1工作区域\n");
     sleep(3);
	 printf("end \n");
    return (void *)&re_val;
    }

int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;
    int *renum; 
    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     if(pthread_join(pthread1,(void**)&renum)!=0)
      {
		  perror("fail to pthread_join");
		  exit(1);
		  }
       printf("renum = %d\n",*(int *)renum);
     return 0;
 }

运行结果

 主线程工作区域
子线程1工作区域
end 
renum = 100

问题1: 当main函数存在一条语句在pthread_join后面,我们发现不管怎么运行,都是先运行完子线程,才运行此语句。但这不是我想要结果,我想要的是各自运行各自的,互不干扰。

因此,我们需要使用另外一种函数——pthread_detach分离函数,同时也自动回收子线程数据。

但是此函数不会等待子线程结束。

十、pthread_detach 

头文件

              #include <pthread.h>
函数:

              int pthread_detach(pthread_t thread);
功能:

             使调用线程与当前进程分离,使其成为一个独立的线程。

             该线程终止时,系统将自动回收它的资源。
参数:
               thread:指定的子线程的id
返回值:
               成功:0
               失败:非0

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
      static int re_val =100;
     printf("子线程1工作区域\n");
     sleep(1);
     printf("子线程1开始工作\n");
     sleep(1);
     printf("子线程1工作结束\n");
    }

int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;

    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }

     #if 0
     if(pthread_join(pthread1,NULL)!=0)
      {
		  perror("fail to pthread_join");
		  exit(1);
		  }
     #endif
     
     if(pthread_detach(pthread1) !=0 )
     {

       perror("fail to pthread_detach");
       exit(1);
      }
     
        
       while(1)
      {
  
         printf("Hello world\n");
         sleep(1);
             }
    
     return 0;
 }

运行结果

 主线程工作区域
Hello world
子线程1工作区域
Hello world
子线程1开始工作
Hello world
子线程1工作结束
Hello world

不等待子线程结束演示过程

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
      static int re_val =100;
     printf("子线程1工作区域\n");
     sleep(1);
     printf("子线程1开始工作\n");
     sleep(1);
     printf("子线程1工作结束\n");
    }

int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;

    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     
     if(pthread_detach(pthread1) !=0 )
     {

       perror("fail to pthread_detach");
       exit(1);
      }
     
      printf("hello\n"); 

      
    
     return 0;
 }

运行结果

 主线程工作区域
hello

结果如上,并没有等待且去执行完子线程,因此,我们常用还是phread_join。只需要把phread_join放在main函数最一个语句上,运行方式与phread_detach,类同。

十一、pthread_self  查询pthrea_ID 

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>



void * pthread_fun1(void *arg)
{
      static int re_val =100;
     printf("子线程1工作区域\n");
	 printf("pthread_ID = %ld\n",(unsigned long )pthread_self());
     sleep(3);
	 printf("end \n");
    return (void *)&re_val;
    }

int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;
    int *renum; 
    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     if(pthread_join(pthread1,(void**)&renum)!=0)
      {
		  perror("fail to pthread_join");
		  exit(1);
		  }
       printf("renum = %d\n",*(int *)renum);
     return 0;
 }

运行结果

 主线程工作区域
子线程1工作区域
pthread_ID = 140598107539200
end 
renum = 100

问题:前面我们都是学习子线程运行完才自动结束,可我不想,我想运行到一半就结束,或者到某个设定的语句就结束,因此,我们需要学习一个线程退出函数——pthread_exit

十二、线程退出函数

头文件

                #include <pthread.h>
函数:

                void pthread_exit(void *retval);
功能:

               退出正在执行的线程
 参数:
             retval:当前线程的退出状态值,

              这个值可以被调用pthread_join函数的线程接收到
 返回值:

                无

12.1无返回值 

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
	
    while(1)
	{
	static int cnt =0;
      cnt++;
     printf("***************\n");		
	 sleep(1);
     if(cnt==5)
	 {
	   pthread_exit(NULL);	 
		 
		}
    }
}
int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;

    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     
	printf("hello world\n");

     if(pthread_join(pthread1,NULL)!=0)
    {
   	  perror("fail to pthread_join");
	  exit(1);
		}		 
     
     return 0;
 }

运行结果

 主线程工作区域
hello world
***************
***************
***************
***************
***************

12.2有返回值(以返回字符串为例) 

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>

void * pthread_fun1(void *arg)
{
  
   static char buf[]="this is pthread";
    while(1)
	{
	static int cnt =0;
      cnt++;
     printf("***************\n");		
	 sleep(1);
     if(cnt==5)
	 {
	   pthread_exit(buf);	 
		 
		}
    }
}
int main()
{ 
    int ret1;
    pthread_t pthread1;
    int data=100;

    printf("主线程工作区域\n");

   if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
   
     {           
         perror("fail to pthread_create1");
         exit(1); 
        }
     
	printf("hello world\n");
   
    char *str;
   
     if(pthread_join(pthread1,(void**)&str)!=0)
    {
   	  perror("fail to pthread_join");
	  exit(1);
		}		 
     printf("str = %s\n",str);
     return 0;
 }

运行结果

 主线程工作区域
hello world
***************
***************
***************
***************
***************
str = this is pthread

你学会了???

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值