1. 引言
在多核处理器日益普及的今天,多线程编程已经成为提高程序性能的重要手段。C语言作为一种高效、底层的编程语言,自然也支持多线程编程。本文将带你深入了解C语言多线程编程的奥秘,并分享一些实用的编程技巧。
2. 线程基础
2.1 线程的概念
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
#include <pthread.h>
void *thread_func(void *arg) {printf("Hello, world!\n");return NULL;
}
int main() {pthread_t tid;pthread_create(&tid, NULL, thread_func, NULL);pthread_join(tid, NULL);return 0;
}
2.2 线程的创建与销毁
C语言中使用pthread_create
函数创建线程,使用pthread_join
函数等待线程结束。
pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, NULL);
2.3 线程的属性
线程拥有属性,如线程分离状态、线程栈大小等。可以通过pthread_attr_t
结构体来设置和获取线程属性。
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);
3. 线程同步
3.1 互斥锁
互斥锁(Mutex)是一种常用的同步机制,用于保护共享资源,防止多个线程同时访问。
pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock(&lock);
// 临界区代码
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);
3.2 条件变量
条件变量(Condition Variable)是一种线程同步机制,用于线程间的条件等待和通知。
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
pthread_cond_wait(&cond, &lock);
// 通知其他线程
pthread_cond_signal(&cond);
pthread_cond_destroy(&cond);
3.3 读写锁
读写锁(Read-Write Lock)允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock);
// 读操作
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
// 写操作
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destroy(&rwlock);
4. 线程通信
4.1 全局变量
全局变量是线程间共享的数据,可以用于线程通信。
int global_var = 0;
void *thread_func(void *arg) {global_var++;printf("global_var: %d\n", global_var);return NULL;
}
4.2 消息队列
消息队列(Message Queue)是一种常用的线程间通信机制,可以用于传递消息和数据。
mqd_t mq = mq_open("/my_queue", O_CREAT | O_RDWR, 0644, NULL);
mq_send(mq, "Hello, world!", 13, 0);
char buffer[64];
mq_receive(mq, buffer, 64, NULL);
mq_close(mq);
mq_unlink("/my_queue");
5. 线程实战技巧
5.1 线程池的魔力
线程池(Thread Pool)是一种预先创建了一定数量的线程,它们在池中等待任务分配的机制。这种设计避免了频繁创建和销毁线程的开销,提高了程序的性能和响应速度,尤其在处理大量短生命周期的任务时效果显著。
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <queue>#define MAX_THREADS 4// 线程池结构体
typedef struct {pthread_mutex_t lock;pthread_cond_t notify;std::queue<void *(*)(void *)> task_queue;pthread_t *threads;bool shutdown;int thread_count;
} ThreadPool;// 线程池任务执行函数
void *threadpool_thread(void *threadpool);// 创建线程池
ThreadPool *threadpool_create(int thread_count);// 销毁线程池
int threadpool_destroy(ThreadPool *pool);// 向线程池添加任务
int threadpool_add_task(ThreadPool *pool, void *(*function)(void *), void *argument);// 线程池线程函数
void *threadpool_thread(void *threadpool) {ThreadPool *pool = (ThreadPool *)threadpool;while (1) {pthread_mutex_lock(&pool->lock);// 等待任务到来或线程池关闭while (pool->task_queue.empty() && !pool->shutdown) {pthread_cond_wait(&pool->notify, &pool->lock);}// 检查线程池是否关闭if (pool->shutdown) {break;}// 获取任务并执行void *(*func)(void *) = pool->task_queue.front();pool->task_queue.pop();pthread_mutex_unlock(&pool->lock);func(NULL);}pthread_mutex_unlock(&pool->lock);return NULL;
}// 创建线程池
ThreadPool *threadpool_create(int thread_count) {ThreadPool *pool = new ThreadPool;pool->thread_count = thread_count;pool->shutdown = false;pool->task_queue = std::queue<void *(*)(void *)>();pthread_mutex_init(&pool->lock, NULL);pthread_cond_init(&pool->notify, NULL);pool->threads = new pthread_t[thread_count];for (int i = 0; i < thread_count; i++) {pthread_create(&pool->threads[i], NULL, threadpool_thread, pool);}return pool;
}// 销毁线程池
int threadpool_destroy(ThreadPool *pool) {if (pool->shutdown) {return -1;}pool->shutdown = true;// 唤醒所有等待的线程pthread_cond_broadcast(&pool->notify);for (int i = 0; i < pool->thread_count; i++) {pthread_join(pool->threads[i], NULL);}pthread_mutex_destroy(&pool->lock);pthread_cond_destroy(&pool->notify);delete[] pool->threads;delete pool;return 0;
}// 向线程池添加任务
int threadpool_add_task(ThreadPool *pool, void *(*function)(void *), void *argument) {pthread_mutex_lock(&pool->lock);pool->task_queue.push(function);pthread_cond_signal(&pool->notify);pthread_mutex_unlock(&pool->lock);return 0;
}// 示例任务函数
void *my_task(void *arg) {printf("Task is running\n");sleep(1);return NULL;
}// 主函数
int main() {ThreadPool *pool = threadpool_create(MAX_THREADS);for (int i = 0; i < 10; i++) {threadpool_add_task(pool, my_task, NULL);}sleep(5); // 等待任务完成threadpool_destroy(pool);return 0;
}
在这个示例中,我们创建了一个简单的线程池,它可以执行通过threadpool_add_task
添加的任务。每个线程都会在任务队列中获取任务并执行,直到线程池被关闭。这种设计使得线程可以复用,减少了线程创建和销毁的开销,非常适合于处理大量短生命周期的并发任务。
5.2 线程局部存储
线程局部存储(Thread-Local Storage,TLS)是一种机制,允许每个线程拥有自己的变量副本,这样可以避免在多线程环境中对全局变量进行同步。
__thread int tls_var = 0;void *thread_func(void *arg) {tls_var++;printf("Thread %ld's tls_var: %d\n", pthread_self(), tls_var);return NULL;
}
5.3 线程安全函数
在设计多线程程序时,需要确保使用的函数是线程安全的。对于那些不是线程安全的函数,可以通过加锁或其他同步机制来保证线程安全。
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;int thread_safe_sum(int a, int b) {pthread_mutex_lock(&lock);int result = a + b;pthread_mutex_unlock(&lock);return result;
}
6. 并发性能优化
6.1 线程亲和性
线程亲和性(Thread Affinity)是指将线程绑定到特定的CPU核心上,以减少线程在核心之间的迁移,提高缓存利用率。
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset); // 绑定到第一个核心pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);pthread_create(&tid, &attr, thread_func, NULL);
pthread_attr_destroy(&attr);
6.2 lock-free 编程
lock-free 编程是一种避免使用锁的并发编程技术,通常涉及到原子操作和内存顺序。
#include <stdatomic.h>atomic_int counter = 0;void *thread_func(void *arg) {for (int i = 0; i < 1000000; i++) {atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);}return NULL;
}
7. 结语
C语言的多线程编程是一个复杂但强大的工具,它可以让你的程序充分发挥多核处理器的性能。通过理解和运用线程同步、通信和性能优化技巧,你可以构建出高效、响应迅速的多线程应用程序。记住,多线程编程是提升性能的利器,但也要小心处理同步和竞争条件,以确保程序的正确性和稳定性。