1. 线程的取消
意义
随时终止一个线程的执行。
函数
#include <pthread.h> int pthread_cancel(pthread_t thread);
pthread_t thread
:要取消的线程 ID。
返回值
成功时返回 0。
失败时返回非零错误码。
注意
线程的取消需要有取消点,取消点通常是阻塞的系统调用。线程在取消点处才会响应取消请求。
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 线程执行函数 void *thread_function(void *arg) { while (1) { printf("Thread is running, TID: %lu\n", pthread_self()); sleep(1); } return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(2); pthread_cancel(tid); printf("Thread canceled\n"); return 0; }
手动设置取消点
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 线程执行函数 void *thread_function(void *arg) { while (1) { printf("Thread is running, TID: %lu\n", pthread_self()); sleep(1); pthread_testcancel(); // 手动设置取消点 } return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(2); pthread_cancel(tid); printf("Thread canceled\n"); return 0; }
设置取消使能或禁止
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 线程执行函数 void *thread_function(void *arg) { int oldstate; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); while (1) { printf("Thread is running, TID: %lu\n", pthread_self()); sleep(1); } pthread_setcancelstate(oldstate, NULL); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(2); pthread_cancel(tid); printf("Thread canceled\n"); return 0; }
设置取消类型
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 线程执行函数 void *thread_function(void *arg) { int oldtype; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); while (1) { printf("Thread is running, TID: %lu\n", pthread_self()); sleep(1); } pthread_setcanceltype(oldtype, NULL); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(2); pthread_cancel(tid); printf("Thread canceled\n"); return 0; }
2. 运行段错误调试
可以使用 gdb 调试。
gdb ./yourapp (gdb) run # 等待出现 Thread 1 "pcancel" received signal SIGSEGV, Segmentation fault. (gdb) bt
示例代码
#include <stdio.h> int main() { int *ptr = NULL; *ptr = 10; // 会导致段错误 return 0; }
3. 线程的清理
必要性
当线程非正常终止时,需要清理一些资源,如释放内存、关闭文件等。
函数
#include <pthread.h> void pthread_cleanup_push(void (*routine) (void *), void *arg) void pthread_cleanup_pop(int execute)
pthread_cleanup_push
:注册一个清理函数,在线程退出时自动调用。pthread_cleanup_pop
:取消注册一个清理函数。如果execute
为非零值,则立即执行清理函数。
示例代码
#include <stdio.h> #include <pthread.h> // 清理函数 void cleanup_function(void *arg) { printf("Cleanup function called, arg: %s\n", (char *)arg); } // 线程执行函数 void *thread_function(void *arg) { pthread_cleanup_push(cleanup_function, "cleanup argument"); printf("Thread is running, TID: %lu\n", pthread_self()); sleep(2); pthread_exit(NULL); pthread_cleanup_pop(0); return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(3); return 0; }
4. 线程的互斥和同步
临界资源概念
不能同时被多个线程访问的资源,例如写文件、打印机等。同一时间只能有一个线程访问。
必要性
防止多个线程同时访问临界资源导致数据不一致或资源竞争。
man 手册找不到 pthread_mutex_xxxxxxx 的解决方法
apt-get install manpages-posix-dev
5.互斥锁的创建和销毁
动态方式
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t *restrict mutex
:指向互斥锁的指针。const pthread_mutexattr_t *restrict attr
:互斥锁属性指针,通常传 NULL 使用默认属性。
静态方式
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
锁的销毁
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥锁的使用
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_lock
:尝试加锁,如果锁已被占用,则阻塞等待。pthread_mutex_unlock
:释放锁。pthread_mutex_trylock
:尝试加锁,如果锁已被占用,则立即返回。
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex; // 线程执行函数 void *thread_function(void *arg) { pthread_mutex_lock(&mutex); printf("Thread is running, TID: %lu\n", pthread_self()); sleep(2); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_mutex_init(&mutex, NULL); pthread_t tid1, tid2; pthread_create(&tid1, NULL, thread_function, NULL); pthread_create(&tid2, NULL, thread_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(3); pthread_mutex_destroy(&mutex); return 0; }
6. 读写锁
必要性
提高线程执行效率,允许多个线程同时读取资源,但写操作需要独占。
特性
写者使用写锁,如果当前没有读者或写者,写者立即获得写锁;否则写者等待。
读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待。
同一时刻只有一个线程可以获得写锁,但可以有多个线程获得读锁。
读写锁在写锁状态时,所有试图加锁的线程都会被阻塞。
读写锁在读锁状态时,如果有写者试图加写锁,之后的其他线程的读锁请求会被阻塞。
函数
#include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_rwlock_t rwlock; // 读线程执行函数 void *reader_function(void *arg) { pthread_rwlock_rdlock(&rwlock); printf("Reader is running, TID: %lu\n", pthread_self()); sleep(2); pthread_rwlock_unlock(&rwlock); return NULL; } // 写线程执行函数 void *writer_function(void *arg) { pthread_rwlock_wrlock(&rwlock); printf("Writer is running, TID: %lu\n", pthread_self()); sleep(2); pthread_rwlock_unlock(&rwlock); return NULL; } int main() { pthread_rwlock_init(&rwlock, NULL); pthread_t tid1, tid2, tid3, tid4; pthread_create(&tid1, NULL, reader_function, NULL); pthread_create(&tid2, NULL, reader_function, NULL); pthread_create(&tid3, NULL, writer_function, NULL); pthread_create(&tid4, NULL, writer_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(3); pthread_rwlock_destroy(&rwlock); return 0; }
7. 死锁
概念
两个或多个线程互相等待对方释放资源,导致无法继续执行。
避免方法
尽量减少锁的使用,最好使用一把锁。
调整锁的获取顺序,确保所有线程按相同顺序获取锁。
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex1, mutex2; // 线程1执行函数 void *thread1_function(void *arg) { pthread_mutex_lock(&mutex1); printf("Thread1 locked mutex1\n"); sleep(1); pthread_mutex_lock(&mutex2); printf("Thread1 locked mutex2\n"); pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); return NULL; } // 线程2执行函数 void *thread2_function(void *arg) { pthread_mutex_lock(&mutex2); printf("Thread2 locked mutex2\n"); sleep(1); pthread_mutex_lock(&mutex1); printf("Thread2 locked mutex1\n"); pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); return NULL; } int main() { pthread_mutex_init(&mutex1, NULL); pthread_mutex_init(&mutex2, NULL); pthread_t tid1, tid2; pthread_create(&tid1, NULL, thread1_function, NULL); pthread_create(&tid2, NULL, thread2_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(3); pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); return 0; }
避免死锁的示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex1, mutex2; // 线程1执行函数 void *thread1_function(void *arg) { pthread_mutex_lock(&mutex1); printf("Thread1 locked mutex1\n"); sleep(1); pthread_mutex_lock(&mutex2); printf("Thread1 locked mutex2\n"); pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); return NULL; } // 线程2执行函数 void *thread2_function(void *arg) { pthread_mutex_lock(&mutex1); printf("Thread2 locked mutex1\n"); sleep(1); pthread_mutex_lock(&mutex2); printf("Thread2 locked mutex2\n"); pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); return NULL; } int main() { pthread_mutex_init(&mutex1, NULL); pthread_mutex_init(&mutex2, NULL); pthread_t tid1, tid2; pthread_create(&tid1, NULL, thread1_function, NULL); pthread_create(&tid2, NULL, thread2_function, NULL); printf("Main process, PID: %d\n", getpid()); sleep(3); pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); return 0; }
8、条件变量
应用场景
生产者消费者问题,是线程同步的一种手段。
必要性
为了实现等待某个资源,让线程休眠。提高运行效率。
函数说明
#include <pthread.h> // 等待条件变量,会自动释放互斥锁,等待被唤醒 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); // 等待条件变量,会自动释放互斥锁,等待被唤醒,超时则返回 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); // 唤醒一个等待的线程 int pthread_cond_signal(pthread_cond_t *cond); // 唤醒所有等待的线程 int pthread_cond_broadcast(pthread_cond_t *cond);
使用步骤
1. 初始化
静态初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 初始化条件变量 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥量
动态初始化:
pthread_cond_init(&cond, NULL);
2. 生产资源线程
pthread_mutex_lock(&mutex); // 开始产生资源 pthread_cond_signal(&cond); // 通知一个消费线程 // 或者 pthread_cond_broadcast(&cond); // 广播通知多个消费线程 pthread_mutex_unlock(&mutex);
3. 消费者线程
pthread_mutex_lock(&mutex); while (/* 没有资源 */) { // 防止惊群效应 pthread_cond_wait(&cond, &mutex); } // 有资源了,消费资源 pthread_mutex_unlock(&mutex);
注意事项
pthread_cond_wait(&cond, &mutex)
,在没有资源等待时是先unlock
休眠,等资源到了,再lock
。所以pthread_cond_wait
和pthread_mutex_lock
必须配对使用。如果
pthread_cond_signal
或者pthread_cond_broadcast
早于pthread_cond_wait
,则有可能会丢失信号。pthread_cond_broadcast
信号会被多个线程收到,这叫线程的惊群效应。所以需要加上判断条件while
循环。
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int resource_ready = 0; // 生产者线程 void *producer(void *arg) { printf("Producer: Producing resource...\n"); sleep(1); pthread_mutex_lock(&mutex); resource_ready = 1; printf("Producer: Resource is ready\n"); pthread_cond_signal(&cond); // 唤醒消费者线程 pthread_mutex_unlock(&mutex); return NULL; } // 消费者线程 void *consumer(void *arg) { printf("Consumer: Waiting for resource...\n"); pthread_mutex_lock(&mutex); while (!resource_ready) { // 防止惊群效应 pthread_cond_wait(&cond, &mutex); } printf("Consumer: Resource consumed\n"); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t prod_tid, cons_tid; pthread_create(&prod_tid, NULL, producer, NULL); pthread_create(&cons_tid, NULL, consumer, NULL); pthread_join(prod_tid, NULL); pthread_join(cons_tid, NULL); return 0; }
9、线程池概念和使用
概念
通俗的讲就是一个线程的池子,可以循环的完成任务的一组线程集合。
必要性
我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设 T1 为创建线程时间,T2 为在线程任务执行时间,T3 为线程销毁时间,当 T1+T3 > T2,这时候就不划算了,使用线程池可以降低频繁创建和销毁线程所带来的开销,任务处理时间比较短的时候这个好处非常显著。
线程池的基本结构
任务队列,存储需要处理的任务,由工作线程来处理这些任务。
线程池工作线程,它是任务队列任务的消费者,等待新任务的信号。
线程池的实现
1. 创建线程池的基本结构
typedef struct Task { void (*func)(void *arg); void *arg; struct Task *next; } Task; typedef struct ThreadPool { pthread_mutex_t lock; pthread_cond_t cond; Task *head; Task *tail; int thread_count; int queue_size; int shutdown; } ThreadPool;
2. 线程池的初始化
ThreadPool *pool_init(int thread_count) { ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool)); pool->lock = PTHREAD_MUTEX_INITIALIZER; pool->cond = PTHREAD_COND_INITIALIZER; pool->head = NULL; pool->tail = NULL; pool->thread_count = thread_count; pool->queue_size = 0; pool->shutdown = 0; for (int i = 0; i < thread_count; i++) { pthread_t tid; pthread_create(&tid, NULL, worker, pool); } return pool; }
3. 线程池添加任务
void pool_add_task(ThreadPool *pool, void (*func)(void *arg), void *arg) { pthread_mutex_lock(&pool->lock); Task *task = (Task *)malloc(sizeof(Task)); task->func = func; task->arg = arg; task->next = NULL; if (pool->tail == NULL) { pool->head = pool->tail = task; } else { pool->tail->next = task; pool->tail = task; } pool->queue_size++; pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->lock); }
4. 实现工作线程
void *worker(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { pthread_mutex_lock(&pool->lock); while (pool->head == NULL && !pool->shutdown) { pthread_cond_wait(&pool->cond, &pool->lock); } if (pool->shutdown) { pthread_mutex_unlock(&pool->lock); break; } Task *task = pool->head; pool->head = task->next; if (pool->head == NULL) { pool->tail = NULL; } pool->queue_size--; pthread_mutex_unlock(&pool->lock); task->func(task->arg); free(task); } return NULL; }
5. 线程池的销毁
void pool_destroy(ThreadPool *pool) { pthread_mutex_lock(&pool->lock); pool->shutdown = 1; pthread_cond_broadcast(&pool->cond); pthread_mutex_unlock(&pool->lock); for (int i = 0; i < pool->thread_count; i++) { pthread_join(pthread_self(), NULL); } while (pool->head != NULL) { Task *task = pool->head; pool->head = task->next; free(task); } pthread_mutex_destroy(&pool->lock); pthread_cond_destroy(&pool->cond); free(pool); }
示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> // 任务函数 void task_function(void *arg) { printf("Task is running, arg: %s\n", (char *)arg); sleep(1); } int main() { ThreadPool *pool = pool_init(4); pool_add_task(pool, task_function, "task1"); pool_add_task(pool, task_function, "task2"); pool_add_task(pool, task_function, "task3"); pool_destroy(pool); return 0; }
###