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);
}