Linux--线程

Posted qnbk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux--线程相关的知识,希望对你有一定的参考价值。

线程

线程概念

线程

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

    在Linux中,站在CPU的角度,能否识别task_struct是进程还是线程?
    答:不能也不需要识别,CPU只关心一个一个的独立执行流

页表映射

线程的优点

  • 创建新线程代价比创建新进程小
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源比进程少
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高行能,将I/O操作重叠,线程可以同时等待不同 的I/O操作
    ps:
    计算密集型:执行流的大部分任务,主要以计算为主:加密解密,排序查找
    I/O密集型:执行流的大部分任务,是以IO为主的:刷磁盘,访问数据库,访问网络

线程的缺点

  • 行能缺失:一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失(增加了额外的同步和调度开销,而可用的资源不变)
  • 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配的细微偏差或因共享了不该共享的变量而导致不良的影响(线程之间是缺乏保护的)
  • 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响
  • 编程难度提高

线程异常

  • 单个线程如果出现除0,野指针等问题导致线程崩溃,进程也会随之崩溃
  • 线程是进程内部的执行分支,线程出现异常,就类似于进程出现异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也随即退出

线程用途

  • 合理使用多线程,能提高CPU密集型程序的执行效率
  • 合理使用多线程,能提高IO密集型程序的用户体验(一边下载视频,一边看视频)

进程和线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程共享进程数据,但也有自己的一部分数据:线程ID,一组寄存器,栈,errno,信号屏蔽字,调度优先级

进程的多个线程共享

同一个地址空间,因此代码段、数据段都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到;线程hi哎共享以下进程资源和环境:文件描述符表,每种信号到处理方式(SIG_DEF,SIG_IGN,自定义的信号处理函数),当前工作目录,用户id和组id

Linux 线程控制

POSIX线程库

  • 与线程有关的函数构成了一个完整的函数系列,大多数函数的名字以"pthread_"开头
  • 头文件<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的"-lpthread"选项

创建线程

#include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
// thread 返回线程id
//attr:设置线程的属性,attr为NULL表示使用默认属性
//start_routine:是一个函数地址,线程启动后要执行的函数
//arg:传给线程启动函数的参数

错误检查:

  • 一些函数是成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误
  • pthreads函数出错时不会设置全局函数errno,而是将错误代码通过返回值返回
  • pthread提供了线程内的errno变量,以支持它使用的errno代码对于pthread函数的错误,建议通过返回值判定,因为读取返回值比读取线程内的errno变量的开销小

创建一个线程

Makefile

mythread:mythread.c
		gcc -o $@ $^  -lpthread
.PHONY:clean
clean:
		rm -f mythread

mythread.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  while(1)
    printf("%s : pid : %d,ppid:%d\\n",msg,getpid(),getppid());
    sleep(1);
  

int main()

  pthread_t tid;
  pthread_create(&tid,NULL,Routine,(void*)"thread 1");
  //主线程 
  
  while(1)
    printf("main thread :pid:%d ppid:%d\\n",getpid(),getppid());
    sleep(2);
  
  return 0;


-L 显示轻量级进程

创建多个线程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  while(1)
    printf("%s : pid : %d,ppid:%d\\n",msg,getpid(),getppid());
    sleep(1);
  

int main()

  pthread_t tid[5];
  int i;
  for(i = 0;i < 5 ;i++)
  char buffer[32];
  sprintf(buffer,"thread %d",i);//格式化控制
  pthread_create(&tid[i],NULL,Routine,(void*)buffer);//Routine函数被重入
  
  //pthread_create(&tid,NULL,Routine,(void*)"thread 1");
  //主线程 
  
  while(1)
    printf("main thread :pid:%d ppid:%d\\n",getpid(),getppid());
    sleep(2);
  
  return 0;


pthread_self()

获得自身线程id

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  while(1)
    printf("%s : pid : %d,ppid:%d,tid :%lu\\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
  

int main()

  pthread_t tid[5];
  int i;
  for(i = 0;i < 5 ;i++)
    char buffer[32];
    sprintf(buffer,"thread %d",i);//格式化控制
    pthread_create(&tid[i],NULL,Routine,(void*)buffer);//Routine函数被重入
    printf("%s tid is %lu\\n",buffer,tid[i]);
  
  //pthread_create(&tid,NULL,Routine,(void*)"thread 1");
  //主线程 
  
  while(1)
    printf("main thread :pid:%d ppid:%d,tid :%lu\\n",getpid(),getppid(),pthread_self());
    sleep(2);
  
  return 0;


线程等待

为什么需要线程等待

  • 已经退出的进程,其空间没有被释放,仍在进程的地址空间内
  • 创建新的进程不会复用刚才退出线程的地址空间

pthread_join()

等待线程结束

#include <pthread.h>
	int pthread_join(pthread_t thread, void **retval);
	//thread:需要等待线程的线程id
	//retval:拿到被等待线程退出时对应的退出码
	
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  int count = 0;
  while(count < 3)
    printf("%s : pid : %d,ppid:%d,tid :%lu\\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
  
  return (void*)0;

int main()

  pthread_t tid[5];
  int i;
  for(i = 0;i < 5 ;i++)
    char *buffer = (char*)malloc(20);
    sprintf(buffer,"thread %d",i);//格式化控制
    pthread_create(&tid[i],NULL,Routine,(void*)buffer);//Routine函数被重入
    printf("%s tid is %lu\\n",buffer,tid[i]);
  
  //pthread_create(&tid,NULL,Routine,(void*)"thread 1");
  //主线程 
  
  printf("main thread :pid:%d ppid:%d,tid :%lu\\n",getpid(),getppid(),pthread_self());
  for(i = 0;i < 5;i++)
    pthread_join(tid[i],NULL);
    printf("thread :%d[%lu] quit!\\n",i,tid[i]);
  
  return 0;


void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  int count = 0;
  while(count < 3)
    printf("%s : pid : %d,ppid:%d,tid :%lu\\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
  
  return (void*)10;

int main()

  pthread_t tid[5];
  int i;
  for(i = 0;i < 5 ;i++)
    char *buffer = (char*)malloc(20);
    sprintf(buffer,"thread %d",i);//格式化控制
    pthread_create(&tid[i],NULL,Routine,(void*)buffer);//Routine函数被重入
    printf("%s tid is %lu\\n",buffer,tid[i]);
  
  //pthread_create(&tid,NULL,Routine,(void*)"thread 1");
  //主线程 
  
  printf("main thread :pid:%d ppid:%d,tid :%lu\\n",getpid(),getppid(),pthread_self());
  for(i = 0;i < 5;i++)
    void *ret = NULL;
    pthread_join(tid[i],&ret);
    printf("thread :%d[%lu] quit!,code :%d\\n",i,tid[i],(int)ret);
  
  return 0;



多线程需要考虑异常,但是做不到-》有异常直接退出
调用该函数的线程将挂起等待,直到id为thread的线程终止

  • 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值
  • 如果thread线程被别的线程调用pthread_cancel异常终止,retval所指向的单元里存放的是常数PTHREAD_CANCELED
  • 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数
  • retval可为NULL

pthread_t

pthread_t类型的id,本质是一个进程地址空间上的一个地址

线程终止

只讨论正常终止
终止某个线程而不终止整个:

  • 从线程函数return
  • 调用pthread_exit()终止自己
  • 调用pthread_cancel()终止同一进程中的其他线程

pthread_exit()

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  int count = 0;
  while(count < 3)
    printf("%s : pid : %d,ppid:%d,tid :%lu\\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
  
  pthread_exit((void*)11);
 // exit(10);//终止进程


int main()

  pthread_t tid[5];
  int i;
  for(i = 0;i < 5 ;i++)
    char *buffer = (char*)malloc(20);
    sprintf(buffer,"thread %d",i);//格式化控制
    pthread_create(&tid[i],NULL,Routine,(void*)buffer);//Routine函数被重入
    printf("%s tid is %lu\\n",buffer,tid[i]);
  
  //主线程 
  
  printf("main thread :pid:%d ppid:%d,tid :%lu\\n",getpid(),getppid(),pthread_self());
  
  for(i = 0;i < 5;i++)
    void *ret = NULL;
    pthread_join(tid[i],&ret);
    printf("thread :%d[%lu] quit!,code :%d\\n",i,tid[i],(int)ret);
  
  
  return 0;



pthread_exit或return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了

pthread_cancel()

 #include <pthread.h>
	int pthread_cancel(pthread_t thread);

1、取消自己线程

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void *Routine(void *arg)

  //新线程 
  char *msg = (char*)arg;
  int count = 0;
  while(count < 3)
    printf("%s : pid : %d,ppid:%d,tid :%lu\\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
  
  int ret = pthread_cancel(pthread_self());//取消自己
  printf("pthread_cancel ret:%d\\n",ret);//成功返回0
  //pthread_exit((void*)11);
 // exit(10);//终止进程
  //return (void*)10;

int main()

  pthread_t tid[5Linux 多线程:线程安全之同步与互斥的理解

Linux内核自旋锁

linux网络编程-posix条件变量(40)

临界区与互斥量区别

linux线程-使用信号量同步

Linux内核设计与实现——内核同步