6、linux c 线程 -下

发布于:2025-03-24 ⋅ 阅读:(29) ⋅ 点赞:(0)

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. 死锁

概念

两个或多个线程互相等待对方释放资源,导致无法继续执行。

避免方法

  1. 尽量减少锁的使用,最好使用一把锁。

  2. 调整锁的获取顺序,确保所有线程按相同顺序获取锁。

示例代码

#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);

注意事项

  1. pthread_cond_wait(&cond, &mutex),在没有资源等待时是先 unlock 休眠,等资源到了,再 lock。所以 pthread_cond_waitpthread_mutex_lock 必须配对使用。

  2. 如果 pthread_cond_signal 或者 pthread_cond_broadcast 早于 pthread_cond_wait,则有可能会丢失信号。

  3. 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. 任务队列,存储需要处理的任务,由工作线程来处理这些任务。

  2. 线程池工作线程,它是任务队列任务的消费者,等待新任务的信号。

线程池的实现

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;
}

###