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