69天探索操作系统-第8天:线程创建和管理 - POSIX 线程实现

120 阅读8分钟

thread1.webp

1.介绍

POSIX 线程(Pthreads)是一种线程创建和同步的标准编程接口。本文提供了使用 Pthreads API 实现和管理线程的深度论述,并附有实用例子和最佳实践。

2.POSIX 线程概述

核心概念

  • Thread ID (pthread_t)
  • 线程属性 (pthread_attr_t)
  • 同步对象
  • 线程本地存储

API组件

#include <pthread.h>

// Core threading functions
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                  void *(*start_routine) (void *), void *arg);
int pthread_join(pthread_t thread, void **retval);
int pthread_exit(void *retval);

image.png

3. 线程创建和初始化

线程属性

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

void* thread_function(void* arg) {
    printf("Thread executing with argument: %d\n", *(int*)arg);
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;
    int arg = 42;

    // Initialize attributes
    pthread_attr_init(&attr);

    // Set stack size (1MB)
    pthread_attr_setstacksize(&attr, 1024 * 1024);

    // Set detach state
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    // Create thread with attributes
    if (pthread_create(&thread, &attr, thread_function, &arg) != 0) {
        perror("Thread creation failed");
        exit(1);
    }

    // Clean up attributes
    pthread_attr_destroy(&attr);

    // Wait for thread completion
    pthread_join(thread, NULL);

    return 0;
}

4.线程管理操作

Joining 线程

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

void* return_value_thread(void* arg) {
    int* result = malloc(sizeof(int));
    *result = 42;
    return (void*)result;
}

int main() {
    pthread_t thread;
    void* result;

    pthread_create(&thread, NULL, return_value_thread, NULL);
    
    // Wait for thread and get return value
    pthread_join(thread, &result);
    
    printf("Thread returned: %d\n", *(int*)result);
    free(result);

    return 0;
}

分离线程

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

void* detached_thread(void* arg) {
    printf("Detached thread running\n");
    sleep(2);
    printf("Detached thread finishing\n");
    pthread_exit(NULL);
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_create(&thread, &attr, detached_thread, NULL);
    pthread_attr_destroy(&attr);

    printf("Main thread continuing...\n");
    sleep(3);  // Wait to see detached thread output
    printf("Main thread exiting\n");

    return 0;
}

取消线程

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

void* cancellable_thread(void* arg) {
    int i = 0;
    while (1) {
        printf("Thread iteration %d\n", ++i);
        sleep(1);
        pthread_testcancel();  // Cancellation point
    }
    return NULL;
}

int main() {
    pthread_t thread;
    
    pthread_create(&thread, NULL, cancellable_thread, NULL);
    sleep(3);  // Let thread run for 3 seconds
    
    printf("Cancelling thread...\n");
    pthread_cancel(thread);
    
    pthread_join(thread, NULL);
    printf("Thread cancelled and joined\n");

    return 0;
}

5.线程同步原语

互斥锁

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_resource = 0;

void* increment_with_mutex(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&mutex);
        shared_resource++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    
    pthread_create(&thread1, NULL, increment_with_mutex, NULL);
    pthread_create(&thread2, NULL, increment_with_mutex, NULL);
    
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    
    printf("Final value: %d\n", shared_resource);
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

条件变量

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int ready = 0;

void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    ready = 1;
    printf("Producer: Data is ready\n");
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    while (!ready) {
        printf("Consumer: Waiting for data\n");
        pthread_cond_wait(&cond, &mutex);
    }
    printf("Consumer: Got data\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t prod_thread, cons_thread;
    
    pthread_create(&cons_thread, NULL, consumer, NULL);
    sleep(1);  // Ensure consumer starts first
    pthread_create(&prod_thread, NULL, producer, NULL);
    
    pthread_join(prod_thread, NULL);
    pthread_join(cons_thread, NULL);
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    
    return 0;
}

栅栏

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

#define NUM_THREADS 3
pthread_barrier_t barrier;

void* barrier_thread(void* arg) {
    int id = *(int*)arg;
    
    printf("Thread %d before barrier\n", id);
    pthread_barrier_wait(&barrier);
    printf("Thread %d after barrier\n", id);
    
    return NULL;
}

int main() {
    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];
    
    pthread_barrier_init(&barrier, NULL, NUM_THREADS);
    
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&threads[i], NULL, barrier_thread, &thread_ids[i]);
    }
    
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    
    pthread_barrier_destroy(&barrier);
    return 0;
}

6.线程本地存储

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

pthread_key_t thread_key;

void cleanup_thread_data(void* data) {
    free(data);
}

void* thread_function(void* arg) {
    int* data = malloc(sizeof(int));
    *data = *(int*)arg;
    
    pthread_setspecific(thread_key, data);
    
    // Access thread-local storage
    int* tls_data = pthread_getspecific(thread_key);
    printf("Thread %d: TLS value = %d\n", *((int*)arg), *tls_data);
    
    return NULL;
}

int main() {
    pthread_t threads[3];
    int thread_args[3] = {1, 2, 3};
    
    pthread_key_create(&thread_key, cleanup_thread_data);
    
    for (int i = 0; i < 3; i++) {
        pthread_create(&threads[i], NULL, thread_function, &thread_args[i]);
    }
    
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }
    
    pthread_key_delete(thread_key);
    return 0;
}

7.高级线程管理

线程调度

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

void* priority_thread(void* arg) {
    int policy;
    struct sched_param param;
    
    pthread_getschedparam(pthread_self(), &policy, &param);
    printf("Thread priority: %d\n", param.sched_priority);
    
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;
    struct sched_param param;
    
    pthread_attr_init(&attr);
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr, &param);
    
    pthread_create(&thread, &attr, priority_thread, NULL);
    pthread_join(thread, NULL);
    
    pthread_attr_destroy(&attr);
    return 0;
}

8. 完整实现代码

以下是一个完整的示例,演示了多线程管理概念:

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

#define NUM_THREADS 5
#define QUEUE_SIZE 10

typedef struct {
    int data[QUEUE_SIZE];
    int front;
    int rear;
    pthread_mutex_t mutex;
    pthread_cond_t not_full;
    pthread_cond_t not_empty;
} thread_safe_queue_t;

thread_safe_queue_t queue = {
    .front = 0,
    .rear = 0,
    .mutex = PTHREAD_MUTEX_INITIALIZER,
    .not_full = PTHREAD_COND_INITIALIZER,
    .not_empty = PTHREAD_COND_INITIALIZER
};

void queue_init(thread_safe_queue_t* q) {
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->not_full, NULL);
    pthread_cond_init(&q->not_empty, NULL);
}

void queue_destroy(thread_safe_queue_t* q) {
    pthread_mutex_destroy(&q->mutex);
    pthread_cond_destroy(&q->not_full);
    pthread_cond_destroy(&q->not_empty);
}

void queue_push(thread_safe_queue_t* q, int value) {
    pthread_mutex_lock(&q->mutex);
    
    while ((q->rear + 1) % QUEUE_SIZE == q->front) {
        pthread_cond_wait(&q->not_full, &q->mutex);
    }
    
    q->data[q->rear] = value;
    q->rear = (q->rear + 1) % QUEUE_SIZE;
    
    pthread_cond_signal(&q->not_empty);
    pthread_mutex_unlock(&q->mutex);
}

int queue_pop(thread_safe_queue_t* q) {
    pthread_mutex_lock(&q->mutex);
    
    while (q->front == q->rear) {
        pthread_cond_wait(&q->not_empty, &q->mutex);
    }
    
    int value = q->data[q->front];
    q->front = (q->front + 1) % QUEUE_SIZE;
    
    pthread_cond_signal(&q->not_full);
    pthread_mutex_unlock(&q->mutex);
    
    return value;
}

void* producer(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 5; i++) {
        int value = id * 100 + i;
        queue_push(&queue, value);
        printf("Producer %d: Pushed %d\n", id, value);
        sleep(1);
    }
    return NULL;
}

void* consumer(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 5; i++) {
        int value = queue_pop(&queue);
        printf("Consumer %d: Popped %d\n", id, value);
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t producers[NUM_THREADS];
    pthread_t consumers[NUM_THREADS];
    int thread_ids[NUM_THREADS];
    
    queue_init(&queue);
    
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&producers[i], NULL, producer, &thread_ids[i]);
        pthread_create(&consumers[i],
        NULL, consumer, &thread_ids[i]);
    }

    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(producers[i], NULL);
        pthread_join(consumers[i], NULL);
    }

    queue_destroy(&queue);

    printf("All threads have finished execution\n");
    return 0;
}

代码解析

  1. 线程安全队列
    • 使用互斥锁和条件变量实现了环形队列,以确保线程安全访问。
    • queue_push将一个元素添加到队列中,queue_pop将一个元素从队列中移除。
    • 队列使用条件变量来处理队列满或空的情况。
  2. 生产者与消费者:
    • 生产者生成数据,并将数据推入队列。
    • 消费者从队列中检索数据并对其进行处理。
    • 生产者和消费者都以并发的方式运行,展示了同步性。
  3. 线程管理:
    • 为生产者和消费者创建线程。
    • pthread_join确保主线程等待所有线程完成。

9.常见陷阱和最佳实践

常见陷阱

  1. 竞态条件
    • 当多个线程在没有适当同步的情况下访问共享资源时发生。
    • 使用互斥锁或其他同步原语来避免这种情况。
  2. 死锁
    • 发生在一个或多个线程无限期地等待由另一个线程持有的资源时。
    • 避免循环依赖并使用超时机制。
  3. 资源泄漏
    • 忘记销毁互斥锁、条件变量或线程本地存储会导致资源泄露。
  4. 线程终止不当
    • 线程不应突然终止(例如,使用 pthread_cancel),除非绝对必要。

最佳实践

  1. 尽量减少锁定竞争
    • 减少线程持有锁的时间,以提高性能。
    • 尽可能使用粒度细的锁定或无锁数据结构。
  2. 使用线程池
    • 不要反复创建和销毁线程,使用线程池可以重用线程。
  3. 避免超额使用线程
    • 除非必要,否则不要创建的线程数量不应超过可用CPU核心的数量。
  4. 测试同步问题
    • 使用Valgrind或ThreadSanitizer等工具检测并发条件和死锁。

10.性能优化

线程开销

  1. 创建与销毁
    • 重复创建和销毁线程可能会很昂贵。
    • 使用线程池可以减轻这种开销。
  2. 上下文切换
    • 线程之间频繁的上下文切换会降低性能。
    • 尽量减少线程的数量并使用线程亲和力来将线程绑定到特定的CPU。

优化同步

  1. 减少锁粒度
    • 为共享数据的不同部分使用独立的互斥锁,以减少竞争。
  2. 使用可读可写锁
    • 使用 pthread_rwlock 处理多个线程读取数据但只有少数写入数据的情况。
  3. 避免忙碌等待
    • 使用条件变量或信号灯而不是在循环中旋转。

11.进一步阅读

  1. 书籍:
    • "Programming with POSIX Threads" by David R. Butenhof
    • "Modern Operating Systems" by Andrew S. Tanenbaum
  2. 文档:
    • Pthreads Documentation
    • GNU C Library Pthreads
  3. 在线资源:
    • POSIX Threads Programming
    • Concurrency in C

12. 结论

线程创建和管理是构建高效并发应用程序的必要技能。POSIX 线程(Pthreads)为创建、同步和管理线程提供了强大的 API。通过了解线程属性、同步原语和最佳实践,开发者可以编写可扩展和性能良好的多线程程序。然而,必须注意避免常见的陷阱,如竞态条件和死锁。

13.参考资料

  1. Butenhof, D. R. (1997). Programming with POSIX Threads. Addison-Wesley.
  2. Tanenbaum, A. S. (2014). Modern Operating Systems (4th ed.). Pearson.
  3. Linux Programmer's Manual: man7.org/linux/man-p…
  4. GNU C Library Documentation: www.gnu.org/software/li…
  5. Intel Threading Building Blocks: www.intel.com/content/www…