线程处理—线程创建

线程处理—线程创建

1.Linux 线程简介

Linux 中的线程是指轻量级的执行单元,相比于进程,具有以下特点:

  1. **进程(Process)是正在执行的程序的实例。每个进程都有自己的地址空间、代 码段、数据段和打开的文件描述符等资源。线程(Thread)是进程内的一个执行单元,它 共享相同的地址空间和其他资源,包括文件描述符、信号处理等,但每个线程都有自己的 栈空间。 **
  2. **由于共享地址空间和数据段,同一进程的多线程之间进行数据交换比进程间通信 方便很多,但也由此带来线程同步问题。 **
  3. **同一进程的多线程共享大部分资源,除了每个线程独立的栈空间。这代表线程的 创建、销毁、切换要比进程的创建、销毁、切换的资源消耗小很多,所以多线程比多进程 更适合高并发。 **

2.相关函数

2.1 系统调用pthread_create

线程操作相关函数来源于 pthread 共享库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <pthread.h>
/**
* 创建一个新线程
*
* pthread_t *thread: 指向线程标识符的指针,线程创建成功时,用于存储新创建线程
的线程标识符
* const pthread_attr_t *attr: pthead_attr_t 结构体,这个参数可以用来设置线
程的属性,如优先级、栈大小等。如果不需要定制线程属性,可以传入 NULL,此时线程将采
用默认属性。
* void *(*start_routine)(void *): 一个指向函数的指针,它定义了新线程开始执
行时的入口点。这个函数必须接受一个 void * 类型的参数,并返回 void * 类型的结果
* void *arg: start_routine 函数的参数,可以是一个指向任意类型数据的指针
* return: int 线程创建结果
* 成功 0
* 失败 非 0
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void
*(*start_routine)(void *), void *arg);

每个线程都有一个唯一的标识符(即线程 ID),这个标识符是通过 pthread_t 类型 的变量来表示的,当 pthread_create 成功创建一个线程时,它会将新线程的标识符存储 在 thread 参数指向的位置.

pthread_t 定义在头文件中,实际上是 long 类型(long 和 long int 是相同类型的不同写法)的别名。

1
typedef unsigned long int pthread_t;  

**新线程执行函数的声明为 void ***(*start_routine)(void *),其入参和返回值都 是 void *指针。我们可以将传递给线程函数的参数包装为结构体,并将其指针作为入参, 再在函数内部处理;同理我们可以在线程函数内部将要返回的状态码和返回值包装为结构 体,并将指针作为返回值 return。

2.2系统调用pthread_join()

pthread_join() 是 POSIX 线程(也称为 pthreads)库中一个重要的函数,它用于等待一个线程的终止。当一个线程被创建后,它可能会并行地执行自己的任务,而主线程(或其他线程)可能需要等待这个线程完成其任务后才能继续执行。pthread_join() 函数就提供了这样的同步机制。

1
2
3
#include <pthread.h>  
 
int pthread_join(pthread_t thread, void **retval);

参数

  • thread:这是需要等待的线程的标识符(ID),即之前通过 pthread_create() 函数创建线程时返回的 pthread_t 类型的值。
  • retval:这是一个指向 void 指针的指针,用于接收被等待线程的退出状态。如果 retval 不是 NULL,那么线程的返回值将被存放在它指向的位置。如果不需要接收线程的返回值,可以将此参数设置为 NULL

返回值

  • 成功时,pthread_join() 返回 0
  • **失败时,返回错误码。常见的错误码包括 **EINVAL(表示传入的线程 ID 无效)和 ESRCH(表示没有对应的线程)。

使用场景

pthread_join() 在多线程编程中非常有用,特别是在以下场景中:

  1. 主线程等待子线程完成:在程序的主线程中,可能需要等待一个或多个子线程完成它们的工作。使用 pthread_join() 可以实现这种等待。
  2. 收集子线程的返回值:如果子线程在执行过程中计算了结果,并且这个结果是主线程所需要的,那么可以通过 pthread_join() 函数的 retval 参数来收集这个结果。

3.实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//
// Created by root on 2024/9/16.
//
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUF_LEN 1024

char *buffer;

void *input_thread(void *argv){
   int i=0;
   while(1){
       char getChar= fgetc(stdin);
       if (getChar&&getChar!='\n'){
           buffer[i++]=getChar;
      }
       //缓冲区溢出
       if (i>=BUF_LEN){
           i=0;
      }
  }
}

void *output_thread(void *argv){
   int i=0;
   while(1){
       if (buffer[i]!=0){
           fputc(buffer[i],stdout);
           fputc('\n',stdout);
           buffer[i++]=0;
           if (i>=BUF_LEN){
               i=0;
          }
      }else{
           sleep(1);
      }
  }
}

int main(int argc,char *argv[]){
   //初始化缓冲区
   buffer=(char*)malloc(sizeof(char)*BUF_LEN);

   //声明线程ID
   pthread_t tid_input;
   pthread_t tid_output;

   //创建读线程
   if ((pthread_create(&tid_input,NULL,input_thread,NULL))!=0){
       perror("pthread_create error");
       exit(EXIT_FAILURE);
  }
   //创建写线程
   if ((pthread_create(&tid_output,NULL,output_thread,NULL))!=0){
       perror("pthread_create error");
       exit(EXIT_FAILURE);
  }
   //主线程等待
   pthread_join(tid_input,NULL);
   pthread_join(tid_output,NULL);

   return 0;
}