Linux 线程与pthread库

Linux线程与pthread库

  • 1. 线程和pthread库
  • 2. 线程的终止与退出
  • 3. 为什么需要线程库?
  • 4. 虚拟地址空间与线程库

在Linux系统中,线程控制是多任务编程的核心,而POSIX线程库(pthread库)则是应用层的原生线程库,为开发者提供了丰富的线程控制功能。

1. 线程和pthread库

1.1 线程创建
在Linux系统中,通过pthread库提供的pthread_create函数可以创建新的线程。该函数的原型如下:

在这里插入图片描述

参数:
thread:参数是一个输出型参数,用于获取创建成功的线程ID。
attr:参数用于设置线程的属性,传入NULL表示使用默认属性。
start_routine:参数是一个函数地址,表示线程启动后要执行的函数。
arg:参数是传给线程例程的参数。

1.2 线程ID获取

每个线程都有一个唯一的线程ID,通过 pthread_self 函数可以获取当前线程的ID。线程ID是一个无符号长整型数。
在这里插入图片描述

1.3. 线程等待

线程的等待是为了确保资源得到释放,避免系统泄漏。使用 pthread_join 函数可以等待线程的结束。
在这里插入图片描述

参数:
thread:参数是被等待线程的ID。
retval:存储线程的返回值(退出状态信息)的位置。
返回值:线程等待成功返回0,失败返回错误码。

下面的示例演示了线程的创建和等待过程:

在编译时,需要链接pthread库。在使用gcc编译时,可以通过在命令行中添加"-pthread"选项来链接pthread库。例如:

gcc -o my_program my_program.c -pthread
#include 
#include   // 添加了这个头文件以使用printf
#include 
#include 

// 线程执行的工作
void* thread_routine(void* arg) {
    char* msg = (char*)arg;
    int n = 10;
    pthread_t tid = pthread_self();
    pid_t pid = getpid();
    pid_t ppid = getppid();
    
    // 在循环中输出线程信息
    while (n--) {
        // 修正了输出格式符
        printf("线程消息:%s pid: %d, ppid:%d, tid: %lu\n", msg, pid, ppid, tid);
        sleep(1);
    }
    
    // 返回线程的退出值
    return ((void*)42);
}

int main() {
    pthread_t tid;  // 存放新线程的ID
    const char* message = "你好,线程";  // 线程的消息
    void* exit_code;  // 用于存放线程的退出值

    // 创建线程
    if (pthread_create(&tid, NULL, thread_routine, (void*)message) != 0) {
        printf("创建线程失败\n");
        return 1;
    }

    // 输出主线程信息
    printf("主线程消息:%s pid: %d, ppid:%d, tid: %lu\n", message, getpid(), getppid(), pthread_self());

    // pthread_join 会阻塞等待子线程结束
    if (pthread_join(tid, &exit_code) == 0) {
        // 修正了输出格式符
        printf("线程返回值:%ld\n", (long)exit_code);
    } else {
        printf("等待线程失败\n");
    }

    return 0;  // 返回主函数的退出值
}

输出:Linux 线程与pthread库_第1张图片

运行时使用 ps -aL 显示当前系统中所有线程的信息

Linux 线程与pthread库_第2张图片

线程ID(TID) 是在进程内唯一的,它用于在同一进程内区分不同的线程
进程ID(PID) 用于在整个系统中唯一标识一个进程
如果PIDLWP相同,表示这个标识是主线程的标识;
如果它们不同,那么通常PID表示整个进程,而LWP表示不同的线程

2. 线程的终止与退出

线程的终止与退出有三种方式:

  1. 使用 return 语句:在线程函数中使用 return 语句可以使线程提前结束,但主线程使用 return 并不会终止整个进程。

示例:

#include 
#include 
#include 

void* thread_routine(void* arg) {
    // 线程执行的工作
    return (void*)42; // 返回退出码 42
}
int main() {
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_routine, NULL) != 0) {
        printf("创建线程失败\n");
        return 1;
    }
    // 等待线程退出,并获取退出码
    void* exit_code;
    pthread_join(tid, &exit_code);
    printf("线程退出码:%ld\n", (long)exit_code);
    return 0;
}
  1. 使用 pthread_exit 函数:允许线程独立终止,而不影响其他线程或整个进程

在这里插入图片描述

参数
retval:线程退出时的退出码信息。

功能
pthread_exit 函数允许线程独立终止,而不影响其他线程或整个进程。线程可以通过调用 pthread_exit 提前结束自己的执行,并传递一个退出码。

示例:

#include 
#include 
#include 

void* thread_routine(void* arg) {
    // 线程执行的工作
    pthread_exit((void*)42);  // 线程退出,返回退出码 42
}
int main() {
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_routine, NULL) != 0) {
        printf("创建线程失败\n");
        return 1;
    }
    // 等待线程退出,并获取退出码
    void* exit_code;
    pthread_join(tid, &exit_code);
    printf("线程退出码:%ld\n", (long)exit_code);
    return 0;
}
  1. 使用 pthread_cancel 函数:用于取消指定线程,被取消的线程可以在任何地方被取消,不仅限于线程函数的返回点。
    在这里插入图片描述

参数:
thread:要取消的线程的线程ID。
功能:
pthread_cancel 函数用于请求取消指定线程。该函数向指定线程发送取消请求,被取消的线程可以在任何地方被取消,不仅限于线程函数的返回点。

示例:

#include 
#include 
#include 

// 线程函数
void* thread_routine(void* arg) {
    // 线程执行的工作
    while(1) {
        printf("Thread ID: %lu\n", pthread_self());
        sleep(1);
        }
}
int main() {
    pthread_t tid;
    // 创建线程
    if (pthread_create(&tid, NULL, thread_routine, NULL) != 0) {
        // 处理线程创建失败
        return 1;
    }
    sleep(3);
    // 取消线程
    pthread_cancel(tid);
    // 等待线程退出
    pthread_join(tid, NULL);
    return 0;
}

3. 为什么需要线程库?

在Linux内核中,线程的管理并不是直接由内核进行的,而是通过引入轻量级进程(Lightweight Process,LWP)的概念来实现。这种设计的核心思想是让内核专注于进程的管理,而将线程的管理交给用户空间的线程库。

  1. 轻量级进程(LWP): 在Linux内核中,每个进程都被分配一个LWP,它实际上是内核对进程的抽象,具有独立的PID,但共享相同的资源。LWP拥有独立的执行流,但线程的管理由用户空间的线程库负责。

  2. 线程概念在用户空间: Linux中没有专门的系统调用用于线程管理,而是通过用户空间的线程库(如pthread库)提供的接口实现。线程库中的函数用于创建、销毁和管理线程,这些线程在内核中对应LWP。

  3. 内核分工与线程库作用: 内核不直接管理线程,而是将管理任务委托给用户空间的线程库。线程库负责调度、切换和同步线程,提供高层次的线程抽象,使开发者能更轻松地进行多线程编程。

  4. 用户空间线程库的作用: 线程库在实现线程管理时可能使用内核提供的机制,但它们是用户空间的实体。线程库简化了开发者对多线程的操作,让他们不必深入了解内核的底层细节。

4. 虚拟地址空间与线程库

Linux 线程与pthread库_第3张图片

  1. 线程库在虚拟地址空间中位于共享区域。具体来说,用户空间的线程库,例如pthread库,通常会被加载到共享区域,这使得它对于同一进程内的所有线程都是可见的。在这个共享区域内,每个新线程都有一个对应的线程控制块(Thread Control Block,TCB),用于存储关于线程的各种信息,确保线程的独立性。

  2. 每个新线程在共享区域都有一个对应的线程控制块(Thread Control Block,TCB)。线程控制块存储了关于线程的各种信息,确保了线程的独立性。在pthread库中,线程ID(tid)指向了对应线程控制块的起始地址。通过这种方式,用户空间的线程库可以有效地管理和操作线程,而不必依赖内核级别的线程管理。线程控制块的存在使得线程库能够追踪线程的状态、优先级、寄存器值等信息,从而更好地协调和调度线程的执行。这种设计使得用户空间的线程库能够更加灵活地处理线程的创建、调度和撤销等操作,降低了对内核的依赖性。

你可能感兴趣的:(linux,c语言,服务器,开发语言)