Linux系统编程(五)多线程

发布于:2024-05-30 ⋅ 阅读:(81) ⋅ 点赞:(0)


  

一、基本知识点

   线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个标准的线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈组成。它与同属一个进程的其他的线程共享进程的资源,如内存空间、文件描述符和其他一些进程相关的属性。
   相比于进程而言,线程更加轻量级,因为它们共享了进程的地址空间以及其他资源,所以线程之间的切换和通信会更加高效。一个进程可以包含多个线程,这些线程可以并发执行,各自独立完成一些特定的任务,或者共同完成一个复杂的任务。主线程结束,则所有线程结束。
在这里插入图片描述

   在多线程编程中,通常会涉及到线程的创建、同步、互斥、通信等操作,以确保线程之间的协调运行,避免资源竞争和数据不一致的问题。

二、线程的编译

   Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第 3 方 posix 标准的 pthread,具有良好的可移植性。在使用了线程的代码编译的时候要在后面加上 -lpthread

例如:gcc test.c -o test -lpthread

三、 线程相关函数

头文件:#include <pthread.h>

在这里插入图片描述

1. 线程的创建

int pthread_create(pthread_t* thread,  pthread_attr_t * attr, void *(*start_routine)(void *),  void * arg);
//pthread_t* thread :线程的句柄,用于区分是哪个线程。
//pthread_attr_t * attr :线程的属性,通常为NULL。
//void *(*start_routine)(void *) :线程所执行的函数,该函数形参和返回值都要为void *。
// void * arg :该值用于传递第三个参数函数的形参,如果不传递可以为NULL。

2. 线程的退出

   函数 pthread_exit 表示线程的退出。其传入的的参数可以被其它线程用 pthread_join 等待函数进行捕获。例如: pthread_exit( (void*)3 ); 表示线程退出,并将 3作为返回值被后面的 pthread_join 函数捕获。该函数的作用就是可以将该线程的计算结果传递给创建它的主线程或者其他线程。
   注意:在使用线程函数时,不能随意使用 exit 退出函数进行出错处理,由于 exit 的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用 pthread_exit 函数来代替进程中的退出函数 exit。

void pthread_exit(void *retval);
//void *retval:作为线程退出时的值。如果不需要捕获该值,可以传入NULL。

3. 线程的等待

   当父线程结束的时候,如果没有 pthread_join 函数等待子线程执行的话,父线程会退出,而主线程的退出会导致进程的退出,故子线程也会退出。
   由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过 wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是 pthread_join 函数。这个函数是一个线程阻塞函数,调用这函数的线程将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。

int pthread_join(pthread_t pthid, void **thread_return);
//pthread_t pthid :线程句柄。
//void **thread_return : 用于接收线程退出的返回值。如果没有需要接收的填NULL。

补充

一般线程的退出和线程的等待是一起使用的。这里我们使用几种参数类型进行举例。
(1)返回整型

int a=10;
pthread_exit((void *) &a);

(2)返回浮点数

int f=10.12;
pthread_exit((void *) &f);

(3)返回字符串

char *string="i love you!";
pthread_exit(string);

(4)返回结构体

typedef struct 
{
   char name[10];
   int  number;
   float score;
}student;
//方式一
student *people;
pthread_exit(people);
//方式二
student people;
pthread_exit((void *)&people);

举例:

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void *func1 (void *arg)
{
  int n=5;  
  float *p=(float *)arg; 
  while(1)
 	{ 
 	 if(n<=0) break; 
     printf("我是子线程,参数为:%f\n", *p);
     n--;
	 sleep(1);
  } 
  pthread_exit( (void*)123 ); 	
}  

int main(int argc, char **argv)
{
   int ret;
   int thread_result;     
   float f_number=0.255;  
   pthread_t thread1;  //线程句柄
   
//创建线程:
   ret=pthread_create(&thread1, NULL, func1, (void *)&f_number); //最后一个参数要取地址!
   if(ret!=0)
	 {
	     perror("pthread_create error!\n");
		 return -1;
	  }
   
//等待子线程退出,并释放其占用的资源
     pthread_join(thread1, (void **)&thread_result);
     printf("thread_result:%d\n", thread_result);
}   

在这里插入图片描述

四、综合举例

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void *fun1(void * arg)    //线程1函数
{
  float *num =(float *)arg;
    while(1)
    {
      sleep(1);
	  printf("这是子线程1,传入的值为%f\n",*num);
    }  
}

void *fun2(void * arg)    //线程2函数
{
  int *num =(int *)arg;
  char *string="pthread2_end"
  while(1)
  {  
     sleep(1);
     printf("这是子线程2,传入的值为%d\n",*num);
     *num--;
     if(*num <0) break;
  }
  pthread_exit((void *)&string);
}


int main()
{
	float pthread1_arg=11.2;  //传入线程函数1的参数
	int pthread2_arg=3;  //传入线程函数2的参数
	
	int ret;
	pthread_t thread1,thread2;//线程句柄
	
	//创建线程1
	ret=pthread_create(&thread1, NULL, fun1, (void *) &pthread1_arg);
	if(ret<0) 
	{
	   perror("pthread1_create error!\n");
	   return -1;
	}
	
	//创建线程2
	ret=pthread_create(&thread2, NULL, fun2, (void *) &pthread2_arg);
	if(ret<0) 
	{
	   perror("pthread2_create error!\n");
	   return -1;
	}
	
	while(1)
	{
	  sleep(1);
	  printf("我是主线程\n");
	}
	
	return 0;
}

在这里插入图片描述