跳到主要内容

Linux 线程入门

1. 基本概念

Linux 线程是一种程序执行的方式,允许多个执行流在单个进程中并发执行。这些执行流称为线程,并且它们共享进程中的所有资源,包括内存和打开的文件。因此,多线程可以让程序更有效地利用多核处理器的计算能力,并且可以程序同时执行多个任务。

Linux线程是由内核管理的,这意味着它们与其他进程共享内核的资源,如内存管理器和系统调用。由于它们共享相同的地址空间,因此多个线程可以访问相同的内存区域,并且它们可以通过共享内存来通信。

为了创建和使用Linux线程,我们需要使用一些特定的系统调用,这些系统调用可以让我们创建和管理线程,并且可以让线程之间同步和通信。

2. 线程创建API

pthread_create() 是Linux线程库中的一个函数,用于创建新的线程。该函数原型为:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

该函数需要四个参数:

  • thread:指向线程标识符的指针。当新线程被创建时,该标识符将被填充。
  • attr:指向线程属性对象的指针。可以使用该对象来设置线程的各种属性,例如它的堆栈大小或分离状态。如果不想指定线程属性,则可以将该参数设置为NULL。
  • start_routine:指向线程的入口函数的指针。新线程将从该函数开始执行,直到该函数返回。
  • arg:一个指针,指向给定给线程的参数。如果不想指定参数,可以将该参数设置为NULL。

该函数返回一个整数值,如果返回0,则表示创建成功;如果返回其它值,则表示创建失败。

例如,以下是使用 pthread_create 创建新线程的示例代码:

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

void *thread_func(void *arg)
{
// 线程的入口函数
}

int main(int argc, char *argv[])
{
pthread_t thread;
int ret;

ret = pthread_create(&thread, NULL, thread_func, NULL);
if (ret != 0) {
// 创建线程失败
printf("Failed to create thread\n");
exit(1);
}
// 其他代码
}

在上面的示例中,pthread_create函数被用来创建一个新的线程,并将其入口函数设置为 thread_function。该线程将从 thread_function 函数开始执行,并在该函数返回时终止。

3. 线程退出与接合API

3.1 线程退出并接受返回值

pthread_exit() 是 Linux 线程库中的一个函数,允许线程终止并返回值给调用者。该函数原型为:

void pthread_exit(void *value_ptr);

它接受一个参数 value_ptr,该参数是一个指向线程返回的值的指针。

例如,以下是使用 pthread_exit( ) 终止线程的示例代码:

void *thread_func(void *arg)
{
// 线程的入口函数
// 其他代码
pthread_exit(NULL);
}

3.2 线程接合

pthread_join() 是 Linux 线程库中的一个函数,它允许一个线程等待另一个线程完成,并获取该线程的返回值。该函数原型为:

int pthread_join(pthread_t thread, void **value_ptr);

该函数需要两个参数:

  • thread:要等待的线程的标识符。
  • value_ptr:指向用于接收线程返回值的指针的指针。

例如,以下是使用 pthread_join() 等待线程完成并获取它的返回值的示例代码:

#include <pthread.h> // 引入线程库头文件
#include <stdio.h>
#include <stdlib.h> // 引入exit函数的头文件

// 计算结果的函数
void *calculate_result(void *args)
{
// 做一些计算
static int result = 100 + 100; // 计算的结果

// 返回结果
pthread_exit((void *)&result);
}

int main(int argc, char *argv[])
{
pthread_t thread;
int ret;

// 创建线程并在其中运行计算
ret = pthread_create(&thread, NULL, calculate_result, NULL);
if (ret != 0) {
// 如果创建线程失败,打印错误信息并退出程序
printf("Failed to create thread: %d\n", ret);
exit(1); // 使用exit函数来退出程序
}

// 等待线程完成并获取结果
void *result = NULL;
ret = pthread_join(thread, &result);
if (ret != 0) {
// 如果等待线程失败,打印错误信息并退出程序
printf("Failed to join thread: %d\n", ret);
exit(1); // 使用exit函数来退出程序
}

// 对结果进行处理
printf("结果是 %d\n", *(int *)result);

pthread_exit(NULL);
}

4. 线程属性有关API

API功能
pthread_attr_destroy( )销毁线程属性
pthread_attr_getaffinity_np( )获取CPU亲和度
pthread_attr_getdetachstate( )获取分离属性
pthread_attr_getguardsize( )获取栈警戒区大小
pthread_attr_getinheritsched( )获取继承策略
pthread_attr_getschedparam( )获取调度参数
pthread_attr_getschedparam( )获取调度策略
pthread_attr_getscope( )获取竞争范围
pthread_attr_getstack( )获取栈指针和栈大小
pthread_attr getstackaddr( )已弃用
pthread_attr_getstacksize( )获取栈大小
pthread_attr_init( )初始化线程属性
pthread_attr_setaffinity_np()设置CPU亲和度
pthread_attr_setdetachstate( )设置分离属性
pthread_attr_setguardsize()设置栈警戒区大小
pthread_attr_setinheritsched( )设置继承策略
pthread_attr_setschedparam( )设置调度参数
pthread_attr_setschedpolicy( )设置调度策略
pthread_attr_setscope( )设置竞争范围
pthread_attr_setstack( )设置栈的位置和栈大小(慎用)
pthread_attr_setstackaddr( )已弃用
pthread_attr_setstacksize( )设置栈大小

以上API都是针对线程属性操作的,所谓线程属性是类型为pthread_attr_t的变量,设置一个线程的属性时,通过以上相关的函数接口,将需要的属性添加到该类型变量里面,再通过 pthread_create( ) 的第二个参数来创建相应属性的线程。

线程属性变量的使用步骤是:

  • 定义线程属性变量,并且使用pthread_attr_init()初始化。
  • 使用pthread_attr_setxxx()来设置相关的属性。
  • 使用该线程属性变量创建相应的线程。
  • 使用pthread_attr_destroy()销毁该线程属性变量。

4.1 线程初始化

pthread_attr_init() 是一个用于初始化线程属性对象的函数,函数原型为:

int pthread_attr_init(pthread_attr_t *attr);

pthread_attr_init 函数只有一个参数:

  • attr:指向线程属性对象的指针。

该函数返回一个整数值,如果返回0,则表示初始化成功;如果返回其它值,则表示初始化失败。

它会将线程的堆栈大小设置为系统默认值,将线程的分离属性设置为PTHREAD_CREATE_JOINABLE,并将线程的动态优先级设置为默认值。

4.2 设置线程的分离属性

pthread_attr_setdetachstate( ) 用于设置线程的分离状态,分离状态指定线程是否是分离的,即它是否在终止时自动释放它所占用的资源,而不需要等待其他线程进行 join 操作。该函数原型为:

int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);

该函数需要两个参数:

  • attr:指向线程属性对象的指针。可以使用该对象来设置线程的各种属性,包括它的分离状态。

  • detachstate:指定新的分离状态。可以是 PTHREAD_CREATE_DETACHED 或 PTHREAD_CREATE_JOINABLE,分别表示设置线程为分离状态和非分离状态。

该函数返回一个整数值,如果返回0,则表示设置成功;如果返回其它值,则表示设置失败。

例如,以下是使用 thread_attr_setdetachstate() 函数设置分离状态的示例代码:

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

void *thread_func(void *arg)
{
int i;

for (i = 0; i < 5; i++) {
printf("Thread function: %d\n", i);
sleep(1);
}
// 退出当前线程
pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_t thread;
pthread_attr_t attr;
int res;

// 初始化线程属性结构体
res = pthread_attr_init(&attr);
if (res != 0) {
perror("pthread_attr_init() failed");
exit(EXIT_FAILURE);
}

// 设置线程为分离状态, 默认为非分离状态
res = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // PTHREAD_CREATE_JOINABLE
if (res != 0) {
perror("pthread_attr_setdetachstate() failed");
exit(EXIT_FAILURE);
}

// 创建新的线程
res = pthread_create(&thread, &attr, thread_func, NULL);
if (res != 0) {
perror("pthread_create() failed");
exit(EXIT_FAILURE);
}

// 非分离状态可以使用join回收线程资源
res = pthread_join(thread, NULL);
if(res == 0) {
printf("The thread resources are recycled normally\n");
} else {
printf("pthread_join() fail:%s\n", strerror(res));
}

// 销毁线程属性结构体
res = pthread_attr_destroy(&attr);
if (res != 0) {
perror("pthread_attr_destroy() failed");
exit(EXIT_FAILURE);
}

printf("Thread created successfully\n");
// 退出当前线程
pthread_exit(NULL);
}

4.3 获取线程的分离状态

pthread_attr_getdetachstate( ) 函数用于获取线程属性对象中的分离状态。该函数原型为:

int pthread_attr_getdetachstate(pthread_attr_t *attr,int *detachstate);

该函数需要两个参数:

  • attr:指向线程属性对象的指针。通过该对象可以获取线程的各种属性,包括它的分离状态。
  • detachstate:指向存储分离状态的变量的指针。该函数会将线程的分离状态保存到该变量中。

例如,以下是使用pthread_attr_getdetachstate() 函数获取分离状态的示例代码:

int state;
pthread_attr_t attr;
// 获取线程属性对象
pthread_attr_init(&attr);
// 获取线程的分离状态
pthread_attr_getdetachstate(&attr, &state);
// 判断线程是否为分离状态
if (state == PTHREAD_CREATE_DETACHED)
printf("The thread is detached.\n");
else
printf("The thread is not detached.\n");
// 释放线程属性对象
pthread_attr_destroy(&attr);

5. 取消线程

pthread_cancel( ) 用于取消指定的线程。该函数原型为:

int pthread_cancel(pthread_t thread);					// 取消一个线程
int pthread_setcancelstate(int state, int *oldstate); // 用何种方式响应取消状态(默认接收请求)
int pthread_setcanceltype(int type, int *oldtype); // 是否立即响应(默认延迟延迟相、应)

该函数需要一个参数:

  • thread:指定要取消的线程。

该函数返回一个整数值,如果返回0,则表示取消线程成功;如果返回其它值,则表示取消线程失败。

当调用 pthread_cancel( ) 函数时,指定的线程将会收到取消信号。

线程可以通过 pthread_setcancelstate( ) 和 pthread_setcanceltype( ) 函数来设置它的取消状态和取消类型,以决定是否应该立即取消。

例如,以下是使用 pthread_cancel( ) 函数取消线程的示例代码:

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

void *thread_func(void *arg)
{
// 定义这个宏意味着本线不响应取消请求
#ifdef DISABLE
//设置不响应取消请求, 默认是可以响应的
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); //不响应取消请求
#else //代表默认
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //响应取消请求
#endif

// 定义这个宏意味着不在存在取消点
#ifdef ASY
//设置为立即响应(不在有取消点), 默认是延迟相应
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //立即响应
#else //代表默认
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); //延迟响应
#endif

// 线程函数
while (1) {
printf("Thread is running...\n");
sleep(1);
}
// 退出线程
pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
pthread_t thread;
int ret;

// 创建新线程
ret = pthread_create(&thread, NULL, thread_func, NULL);
if (ret != 0) {
// 如果线程创建失败,则输出错误信息并退出
fprintf(stderr, "Error creating thread: %d\n", ret);
exit(EXIT_FAILURE);
}

// 等待一段时间
sleep(5);

// 取消线程
ret = pthread_cancel(thread);
if (ret != 0) {
// 如果取消线程失败,则输出错误信息并退出
fprintf(stderr, "Error canceling thread: %d\n", ret);
exit(EXIT_FAILURE);
} else {
printf("This thread was canceled successfully\n");
}

// 等待线程退出
ret = pthread_join(thread, NULL);
if (ret != 0) {
// 如果等待线程退出失败,则输出错误信息并退出
fprintf(stderr, "Error joining thread: %d\n", ret);
exit(EXIT_FAILURE);
} else {
printf("The thread resources are recycled normally\n");
}

pthread_exit(NULL);
}