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);
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, ¶m);
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, ¶m);
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;
}
代码解析
- 线程安全队列
- 使用互斥锁和条件变量实现了环形队列,以确保线程安全访问。
queue_push将一个元素添加到队列中,queue_pop将一个元素从队列中移除。- 队列使用条件变量来处理队列满或空的情况。
- 生产者与消费者:
- 生产者生成数据,并将数据推入队列。
- 消费者从队列中检索数据并对其进行处理。
- 生产者和消费者都以并发的方式运行,展示了同步性。
- 线程管理:
- 为生产者和消费者创建线程。
pthread_join确保主线程等待所有线程完成。
9.常见陷阱和最佳实践
常见陷阱
- 竞态条件:
- 当多个线程在没有适当同步的情况下访问共享资源时发生。
- 使用互斥锁或其他同步原语来避免这种情况。
- 死锁:
- 发生在一个或多个线程无限期地等待由另一个线程持有的资源时。
- 避免循环依赖并使用超时机制。
- 资源泄漏:
- 忘记销毁互斥锁、条件变量或线程本地存储会导致资源泄露。
- 线程终止不当:
- 线程不应突然终止(例如,使用 pthread_cancel),除非绝对必要。
最佳实践
- 尽量减少锁定竞争:
- 减少线程持有锁的时间,以提高性能。
- 尽可能使用粒度细的锁定或无锁数据结构。
- 使用线程池:
- 不要反复创建和销毁线程,使用线程池可以重用线程。
- 避免超额使用线程:
- 除非必要,否则不要创建的线程数量不应超过可用CPU核心的数量。
- 测试同步问题:
- 使用Valgrind或ThreadSanitizer等工具检测并发条件和死锁。
10.性能优化
线程开销
- 创建与销毁:
- 重复创建和销毁线程可能会很昂贵。
- 使用线程池可以减轻这种开销。
- 上下文切换:
- 线程之间频繁的上下文切换会降低性能。
- 尽量减少线程的数量并使用线程亲和力来将线程绑定到特定的CPU。
优化同步
- 减少锁粒度:
- 为共享数据的不同部分使用独立的互斥锁,以减少竞争。
- 使用可读可写锁:
- 使用 pthread_rwlock 处理多个线程读取数据但只有少数写入数据的情况。
- 避免忙碌等待:
- 使用条件变量或信号灯而不是在循环中旋转。
11.进一步阅读
- 书籍:
- "Programming with POSIX Threads" by David R. Butenhof
- "Modern Operating Systems" by Andrew S. Tanenbaum
- 文档:
- Pthreads Documentation
- GNU C Library Pthreads
- 在线资源:
- POSIX Threads Programming
- Concurrency in C
12. 结论
线程创建和管理是构建高效并发应用程序的必要技能。POSIX 线程(Pthreads)为创建、同步和管理线程提供了强大的 API。通过了解线程属性、同步原语和最佳实践,开发者可以编写可扩展和性能良好的多线程程序。然而,必须注意避免常见的陷阱,如竞态条件和死锁。
13.参考资料
- Butenhof, D. R. (1997). Programming with POSIX Threads. Addison-Wesley.
- Tanenbaum, A. S. (2014). Modern Operating Systems (4th ed.). Pearson.
- Linux Programmer's Manual: man7.org/linux/man-p…
- GNU C Library Documentation: www.gnu.org/software/li…
- Intel Threading Building Blocks: www.intel.com/content/www…