linux-c-multi_thread

2 篇文章 0 订阅
2 篇文章 0 订阅

linux-c 多线程

1. 线程

头文件:#include <pthread.h>
其他头文件:

1.1. 线程函数

每一个线程都有唯一的线程 id ,id的类型为 pthread_t,这个id是一个无符号长整形数,如果想要获取当前线程的id,调用函数:

pthread_t pthread_self(void);

在一个进程中调用线程创建函数,得到一个子线程,需要给每一个创建的线程指定一个处理函数,否则这个子线程无法工作。

int pthread_create(
    pthread_t* thread, // 传出参数,无符号长整形。线程创建成功,会将线程id写入到这个指针指向的内存
    const pthread_attr_t* attr, // 线程的属性,默认实行即可,NULL
    void* (*start_routine)(void*), //函数指针,创建出的子线程的处理动作,也就是该函数在该子线程中执行
    void* arg // 作为实参传递到start_rountine的指针直线函数的内部
);
// 返回值:成功返回0,错误返回对应的错误号

void*指针:指向非具体数据的指针,无类型指针。或者是通用性的一个指针,一般在使用的时候需要通过强制类型转换,可以给其他类型的指针变量赋值。

example1.1.-1

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
// linux: gcc -main.c -o main -lpthread
void* callback(void* arg) {

    for(int i = 0; i < 5; i++) {
        printf("sub thread: i = %d \n", i);
    }
    printf("sub thread: %ld\n", pthread_self()); //获取线程的id
    return NULL;
}

int main() {
    
    pthread_t tid;
    pthread_create(&tid, NULL, callback, NULL);
    for(int i = 0; i < 5; i++) {
        printf("main thread: i = %d \n", i);
    }
    printf("main thread: %ld\n", pthread_self()); //获取线程的id
    
    
    sleep(3);
    return 0;
}

线程退出函数:让某个线程退出,但是不会导致虚拟地址空间的释放(针对主线程)

void pthread_exit(
    void *retval // 线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,写NULL
);

线程回收函数

// 这是一个阻塞函数,子线程在运行这个函数 主线程就会阻塞。 子线程退出,函数解阻塞,回收子线程的资源,类似于回收进程用的函数wait()。每一次调用只能回收一个线程,多个线程不行。
int pthread_join(
    pthread_t thread, //要被回收的子线程id
    void** retval//二级指针,指向一级指针的地址,只一个传出参数,这个地址存储了pthread_exit()传递的数据,如果不需要这个参数,可以指定为NULL
);
// 返回值:成功返回0.失败返回错误信号

example1.1.-2

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

struct Test {
    int num;
    int age;
};

void* callback(void* arg) {
    for(int i = 0; i < 5; i++) {
        printf("sub thread: i = %d \n", i);
    }
    printf("sub thread: %ld\n", pthread_self()); //获取线程的id
    struct Test t;
    t.age = 19;
    t.num = 3;
    pthread_exit(&t); //这里返回了一个局部变量的地址,显然是不合法的
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, callback, NULL);
    printf("main thread: %ld\n", pthread_self()); //获取线程的id
    // sleep(3);
    void* ptr;
    pthread_join(tid, &ptr);
    struct Test* pt = (struct Test*) ptr;
    printf("num = %d, age = %d\n", pt->num, pt->age);
    return 0;
}

这是一段问题代码,子线程(函数)返回了一个局部变量的地址。

解决办法:

  • 将局部变量变成全局变量,使得所有线程都可以访问到。

    • struct Test t;改为static struct Test t;
    • struct Test t;移到main函数外
  • 子线程使用主线程的一块地址空间

    struct Test {
        int num;
        int age;
    };
    void* callback(void* arg) {
        for(int i = 0; i < 5; i++) {
            printf("sub thread: i = %d \n", i);
        }
        printf("sub thread: %ld\n", pthread_self()); //获取线程的id
        struct Test* pt = (struct Test*) arg;
        pt->age = 18;
        pt->num = 3;
        pthread_exit(pt); //这里返回了一个局部变量的地址,显然是不合法的
        return NULL;
    }
    
    int main() {
        struct Test t;
        pthread_t tid;
        pthread_create(&tid, NULL, callback, &t);
        printf("main thread: %ld\n", pthread_self()); //获取线程的id
        // sleep(3);
        void* ptr;
        pthread_join(tid, &ptr);
        printf("num = %d, age = %d\n", t.num, t.age);
        return 0;
    }
    

线程分离函数

调用这个函数后,指定的子线程就和主线程相分离,当子线程退出的时候,其占用的资源才会被释放。不由主线程来回收线程资源。

int pthread_detach(pthread_t thread);

线程取消函数

在某些情况下,在一个线程中杀死另一个线程。是这个函数杀死一个线程的条件:

  • 线程A中调用线程取消函数pthread_cancle(),指定杀死线程B,这时候线程B是死不了的。
  • 在线程B中进程进行一次系统调用(从用户区切换到内核区),否则线程B一直运行
int pthread_cancle(pthread_t thread);

线程id比较

int pthread_equal(pthread_t t1, pthread_t t2); //相等非0,否则0

1.2. 线程同步

多个线程必须按顺序访问同一块内存地址。

1.2.1. 互斥锁

锁的类型:pthread_mutex_t mutex;

初始化锁

int pthread_mutex_init(
    pthread_mutex_t* restrict mutex, //锁的地址
    const pthread_mutexattr_t* restrict attr//锁的属性,一般使用默认属性(NULL)
);

restrict关键字:restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;

释放锁

int pthread_mutex_destory(pthread_mutex_t* mutex);

加锁

int pthread_mutex_lock(pthread_mutex_t* mutex);

这个函数被调用,首先会判断参数mutex的状态是不是锁定状态:

  • 如果没有被锁定,这个线程加锁成功,这个锁会记录哪个线程加锁成功了
  • 如果已经被锁定,这个线程就阻塞在这个锁上。
  • 当这把锁被解开之后,就会解除阻塞。线程通过竞争的方式对这把锁加锁,没抢到锁的线程继续阻塞。

解锁

int pthrtead_mutex_unlock(pthread_mutex_t* mutex);

尝试加锁

int pthread_mutex_trylock(pthread_mutex_t* mutex);
  • 如果这把锁是打开的,则线程加锁成功
  • 如果锁是锁住了,调用这个函数加锁的线程,不会被阻塞,加锁失败直接返回错误号

example1.2.-1

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

int max = 100;
int number = 0;
pthread_mutex_t mutex;
void* read_number(void* args) {
    for (int i = 0; i < max; i++) {
        pthread_mutex_lock(&mutex);
        printf("read: i = %d, id = %ld, number = %d\n", i, pthread_self(), number);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

void* write_number(void* args) {
    for (int i = 0; i < max; i++) {
        pthread_mutex_lock(&mutex);
        srand((unsigned)time(NULL));
        int new_num = rand() % 100;
        printf("write: i = %d, id = %ld, old_number = %d, new_number = %d\n", i, pthread_self(), number, new_num);
        number = new_num;
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}
int main () {
    pthread_t read_t;
    pthread_t write_t;
    pthread_create(&read_t, NULL, read_number, NULL);
    pthread_create(&write_t, NULL, write_number, NULL);
    pthread_join(read_t, NULL);
    pthread_join(write_t, NULL);
    sleep(20);
    pthread_mutex_destroy(&mutex);
    return 0;
}
1.2.2. 读写锁

锁的类型pthread_rwlock_t,有了类型之后就可以创建一把互斥锁了。

pthread_rwlock_t rwlock;

会记录锁的状态

可以设置读写的优先级

应用场景:大量的读操作,用读写锁。其他清空都差不多。

初始化读写锁

int pthread_rwlock_init(
    pthread_rwlock_t* restrict rwlock,
    const pthread_rwlockatt_t* restrict attr
);

释放读写锁

int pthread_rwlock_destory(pthread_rwlock_t* restrict rwlock);

读操作加锁

// 如果锁打开则加锁成功;如果锁已经锁定了读操作,调用依然可以加锁成功,读锁是共享的;入股是写操作被锁定了,调用会阻塞
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);

1.3. 条件变量

条件变量的作用不是处理线程同步,而是进行线程的阻塞。

条件变量的类型为:pthread_cond_t

头文件:pthread.h

初始化

int pthread_cond_init(
	pthread_cond_t* restrict cond,
    pthread_condattr_t* restrict attr
);
int pthread_cond_destory(pthread_cond_t* cond);

线程阻塞

// 阻塞线程函数,哪个线程调用这个函数,哪个线程就会被阻塞
int pthread_cond_wait(
    pthread_cond_t* restrict cond,
    pthread_mutext_t* restrict mutex // 通过这个互斥锁进行线程同步
);
  • 阻塞线程的时候,如果线程已经对互斥锁mutex上锁,那么将会把这把锁打开,这样做是避免死锁
  • 当线程解除阻塞的时候,函数内部会帮助这个线程再次将这个mutex锁上,继续向下访问临界区
// 线程阻塞超时
int pthread_cond_timewait(
	pthread_cont_t* restrict cond,
    pthread_mutext_t* restrict mutex,
    const struct timespec* restrict abstime //阻塞的时长,记录的是从1971.1.1到当前时间的数据,总长度使用秒、纳秒表示
);
// struct timespec的赋值比较麻烦
time_t mytime = time(NULL);
struct timespec tmsp;
tmsp.tv_sec = 0;
tmsp.tv_sec = tiem(NULL) + 100;

// 唤醒阻塞在条件变量上的线程,至少有一个被解除阻塞
int pthread_cond_signal(pthread_cond_t* cond);
// 唤醒阻塞在条件变量上的线程,被阻塞的全部线程都被解除阻塞
int pthread_cond_broadcast(pthread_cond_t* cond);

1.4 信号量

当总的资源数为1时,只用信号量就可以使得线程同步

头文件<semaphore.h>

类型为:sem_t sem

初始化

int sem_init(
    sem_t* sem, //信号量变量地址
    int pshared, //0:线程同步;非0:进程同步
    unsigned int value//初始化当前信号量拥有的资源数(>=0),如果有资源数=0,则线程阻塞
);

阻塞

int sem_wait(sem_t* sem);

当线程调用这个函数,并且sem > 0,线程不会阻塞,线程会占用sem中的一个资源,因此资源数-1,直到sem=0,资源被耗尽,线程就会被阻塞。

int sem_trywait(sem_t* sem);

sem = 0时候不会阻塞,会返回错误号。

int sem_post(sem_t* sem);

调用时 sem + 1;如果其他线程在调用sem_wait,sem_trywait,sem_timewait时被阻塞了,这时候也会解除阻塞状态,获得资源后继续运行下去。

int sem_getvalue(
    sem_t* sem, 
    int* sval  // 传出参数
);

2. 生产者和消费者模型

example2.-1

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

const int max = 50;

struct Node {
    int val;
    struct Node* next;
};

struct Node* head = NULL;
pthread_cond_t cond;
pthread_mutex_t mutex;

void* consumer(void* arg) {
    while (1)
    {
        pthread_mutex_lock(&mutex);
        while (head == NULL) // 如果没有了,就不能消费,线程阻塞
        {
            pthread_cond_wait(&cond, &mutex);
        }
        struct Node* node = head;
        printf("consumer 消费了 val = %d, id = %ld\n", node->val, pthread_self());
        head = head->next;
        free(node);
        pthread_mutex_unlock(&mutex);
        usleep(rand() % 2);
    }
    return NULL;
}
void* producer(void* arg) {
    while (1)
    {
        pthread_mutex_lock(&mutex);
        struct Node* node = (struct Node*)malloc(sizeof(struct Node));
        node->val = rand() % 100;
        node->next = head;
        head = node;
        printf("producer 生产了 val = %d, id = %ld\n", node->val, pthread_self());
        pthread_mutex_unlock(&mutex);
        pthread_cond_broadcast(&cond); // 生产了,唤醒消费者消费
        usleep(rand() % 2);
    }
    return NULL;
}

int main() {
    pthread_t p1[5], p2[5];
    for (int i = 0; i < 5; i++) {
        pthread_create(&p1[i], NULL, consumer, NULL);
        pthread_create(&p2[i], NULL, producer, NULL);
    }
    for (int i = 0;i < 5; i++) {
        pthread_join(p1[i], NULL);
        pthread_join(p2[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

example2.-2

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>

const int max = 50;

struct Node {
    int val;
    struct Node* next;
};

struct Node* head = NULL;
pthread_mutex_t mutex;
sem_t semp;
sem_t semc;

void* consumer(void* arg) {
    while (1)
    {
        // pthread_mutex_lock(&mutex);
        sem_wait(&semc);
        struct Node* node = head;
        printf("consumer 消费了 val = %d, id = %ld\n", node->val, pthread_self());
        head = head->next;
        free(node);
        // pthread_mutex_unlock(&mutex);
        sem_post(&semp);
        usleep(rand() % 2);
    }
    return NULL;
}
void* producer(void* arg) {
    while (1)
    {
        // pthread_mutex_lock(&mutex);
        sem_wait(&semp);
        struct Node* node = (struct Node*)malloc(sizeof(struct Node));
        node->val = rand() % 100;
        node->next = head;
        head = node;
        printf("producer 生产了 val = %d, id = %ld\n", node->val, pthread_self());
        // pthread_mutex_unlock(&mutex);
        sem_post(&semc);
        usleep(rand() % 2);
    }
    return NULL;
}

int main() {
    pthread_t p1[5], p2[5];
    sem_init(&semp, 0, 1); // 最多只有一个生产者同时运行
    sem_init(&semc, 0, 0);
    for (int i = 0; i < 5; i++) {
        pthread_create(&p1[i], NULL, consumer, NULL);
        pthread_create(&p2[i], NULL, producer, NULL);
    }
    for (int i = 0;i < 5; i++) {
        pthread_join(p1[i], NULL);
        pthread_join(p2[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

加锁:互斥锁放到 信号量 的“里面”,放在外面容易产生死锁。

#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>

const int max = 50;

struct Node {
    int val;
    struct Node* next;
};

struct Node* head = NULL;
pthread_mutex_t mutex;
sem_t semp;
sem_t semc;

void* consumer(void* arg) {
    while (1)
    {
        sem_wait(&semc);
        pthread_mutex_lock(&mutex);
        struct Node* node = head;
        printf("consumer 消费了 val = %d, id = %ld\n", node->val, pthread_self());
        head = head->next;
        free(node);
        pthread_mutex_unlock(&mutex);
        sem_post(&semp);
        usleep(rand() % 2);
    }
    return NULL;
}
void* producer(void* arg) {
    while (1)
    {
        
        sem_wait(&semp);
        pthread_mutex_lock(&mutex);
        struct Node* node = (struct Node*)malloc(sizeof(struct Node));
        node->val = rand() % 100;
        node->next = head;
        head = node;
        printf("producer 生产了 val = %d, id = %ld\n", node->val, pthread_self());
        pthread_mutex_unlock(&mutex);
        sem_post(&semc);
        usleep(rand() % 2);
    }
    return NULL;
}

int main() {
    pthread_t p1[5], p2[5];
    sem_init(&semp, 0, 5);
    sem_init(&semc, 0, 0);
    for (int i = 0; i < 5; i++) {
        pthread_create(&p1[i], NULL, consumer, NULL);
        pthread_create(&p2[i], NULL, producer, NULL);
    }
    for (int i = 0;i < 5; i++) {
        pthread_join(p1[i], NULL);
        pthread_join(p2[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

3. 线程池工作原理

线程池主要分为3个部分,这三个部分配合工作:

  1. 任务队列:存储需要处理的任务队列,由工作的线程处理这些任务
    • 通过线程池提供的API函数,将一个待处理的任务添加到任务队列,或者从任务队列中删除
    • 已处理的任务会被人会队列中删除
    • 线程池的使用者,也就是调用线程池函数往任务队列中添加任务的线程就是生产者线程
  2. 工作的线程(任务队列任务的消费者),N个
    • 线程池维护了一定数量的工作线程,他们的作用是不停的读任务队列,从里边取出任务并处理
    • 工作的线程相当于是任务队列的消费者
    • 如果任务队列为空,工作的线程将会阻塞(使用条件变量\信号量)
    • 如果阻塞之后有新的任务,由生产者将阻塞解除,工作线程开始工作
  3. 管理者线程(不处理任务队列的任务),1个
    • 他的任务是周期性的队任务队列中的任务数量以及处于忙状态的工作线程进行检测
      • 任务过多:适当的创建新的线程
      • 任务过少:适当的销毁一些线程

4. 线程池实现

头文件

#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_H_

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

static int g_n = 2;
static int g_t = 3;

// 任务结构体
typedef struct Task Task;
// 线程池结构体
typedef struct ThreadPool ThreadPool;

// 创建线程池
ThreadPool* threadPoolCreate(int min, int max, int queueSize);
// 销毁线程池
int threadPoolDestory(ThreadPool* pool);
// 线程池添加任务
void threadPoolAdd(ThreadPool* pool, void (*func)(void*), void* arg);
// 获取工作的线程个数
int threadPooBusyNum(ThreadPool* pool);
// 获取活着的线程个数
int threadPoolLiveNum(ThreadPool* pool);
// 工作线程函数
void* worker(void* arg);
// 管理线程函数
void* manager(void* arg);
//销毁线程函数
void threadExit(ThreadPool* pool);

#endif

源文件


#include "thread_pool.h"

typedef struct Task {
    void (*function)(void* arg); //函数指针
    void* arg;
} Task;

typedef struct ThreadPool {
    Task* taskQ; // 任务队列, 一个首位相接的队列(循环队列)
    int queueCapacity; // capacity
    int queueSize; // 大小
    int queueFront; // 队头
    int queueRear; //队尾

    pthread_t managerID; // 管理者线程id
    pthread_t* threadIDs; //工作的线程
    int minNum; //最小线程的个数
    int maxNum;
    int busyNum; //工作的线程
    int liveNum; //存活的线程
    int exitNum; //销毁的线程

    pthread_mutex_t mutexPool; //锁住整个线程池
    pthread_mutex_t mutexBusy; // 锁住busyNum
    pthread_cond_t notFull; //任务队列是否满了,生产者条件变量
    pthread_cond_t notEmpty;//任务队列是否空了,消费者条件变量
    int shutdown; //是不是要销毁线程池,1-销毁,0-not
}ThreadPool;


ThreadPool* threadPoolCreate(int min, int max, int queueSize) {
    ThreadPool* pool = (ThreadPool*) malloc(sizeof(ThreadPool));
    printf("threadPoolCreate called... \n"); // 
    do {
        if (pool == NULL) {
            printf("ThreadPool malloc failed.\n");
            break;
        }
        // 初始化结构体的成员
        pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t) * max);
        if (pool->threadIDs == NULL) {
            printf("ThreadPool->threadIDs malloc failed.\n");
            break;
        }

        pool->minNum = min;
        pool->maxNum = max;
        pool->busyNum = 0;
        pool->liveNum = min;
        pool->exitNum = 0;
        if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||
            pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||
            pthread_cond_init(&pool->notFull, NULL) != 0 ||
            pthread_cond_init(&pool->notEmpty, NULL) != 0) {
                printf("ThreadPool initial failed!");
                break;
        }

        pool->taskQ = (Task*)malloc(sizeof(Task) * queueSize);
        pool->queueCapacity = queueSize;
        pool->queueSize = 0;
        pool->queueFront = 0;
        pool->queueRear = 0;
        pool->shutdown = 0;
        // 创建线程
        pthread_create(&pool->managerID, NULL, manager, pool);
        printf("manager 子线程已经创建...\n");
        for (int i = 0 ; i < min; i++) {
            pthread_create(&pool->threadIDs[i], NULL, worker, pool);
        }
        printf("%d条worker子线程已经创建完毕...\n", min);
        return pool;

    } while (0); // 0表示只会执行一次

    // 如果创建失败,就由内向外释放已经创建的指针
    if (pool && pool->threadIDs) free(pool->threadIDs);
    if (pool && pool->taskQ) free(pool->taskQ);
    if (pool) free(pool);
    return NULL;
}


void* worker(void* arg) {
    printf("worker called... \n"); // 
    ThreadPool* pool = (ThreadPool*) arg;
    while (1) {
        pthread_mutex_lock(&pool->mutexPool);
        // 判断当前任务数 
        while (pool->queueSize == 0 && !pool->shutdown) {
            // 阻塞工作线程
            pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
            // 判断是否要销毁线程
            if (pool->exitNum > 0) {
                pool->exitNum--;
                if (pool->liveNum > pool->minNum) {
                    pool->liveNum--;
                    pthread_mutex_unlock(&pool->mutexPool);
                    threadExit(pool);
                }
            }
        }
        // 判断当前线程池是否关闭
        if (pool->shutdown) {
            pthread_mutex_unlock(&pool->mutexPool); // 避免死锁
            threadExit(pool);
        }
        // 从任务队列取出一个任务
        Task task;
        task.function = pool->taskQ[pool->queueFront].function;
        task.arg = pool->taskQ[pool->queueFront].arg;
        // 移动 queueFront 头节点
        pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;
        pool->queueSize--;
        pthread_cond_signal(&pool->notFull);
        pthread_mutex_unlock(&pool->mutexPool);

        printf("thread %ld start working...\n", pthread_self());
        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum++;
        pthread_mutex_unlock(&pool->mutexBusy);
        task.function(task.arg); // 函数指针的方式
        // (*task)(task.arg); // 解引用的方式
                
        free(task.arg);
        task.arg = NULL;
        printf("thread %ld end working...\n", pthread_self());

        pthread_mutex_lock(&pool->mutexBusy);
        pool->busyNum--;
        pthread_mutex_unlock(&pool->mutexBusy);
        sleep(1);
    }
    return NULL;
}


void* manager(void* arg) {
    printf("manager called... \n"); // 
    ThreadPool* pool = (ThreadPool*) arg;
    while (!pool->shutdown)
    {
        sleep(g_t); // 每隔3s检测一次
        // 取出线程池中的任务的数量 和 当前线程池的数量
        pthread_mutex_lock(&pool->mutexPool);
        int queueSize = pool->queueSize;
        int liveNum = pool->liveNum;
        pthread_mutex_unlock(&pool->mutexPool);

        // 取出忙的线程的数量
        pthread_mutex_lock(&pool->mutexBusy);
        int busyNum = pool->busyNum;
        pthread_mutex_unlock(&pool->mutexBusy);

        // 添加任务
        // 任务的个数 > 存活的线程个数 && 存活的线程数 < 最大线程数
        if (queueSize > liveNum && liveNum < pool->maxNum) {
            pthread_mutex_lock(&pool->mutexPool);
            int counter = 0;
            // 从 pool->threadIDs[i] 里找到一个可以存放线程id的位置,并且创建线程
            for (int i = 0; i < pool->maxNum && counter < g_n 
                && pool->liveNum < pool->maxNum; i++) {
                if (pool->threadIDs[i] == 0) { //0表示当前线程池可用
                    pthread_create(&pool->threadIDs[i], NULL, worker, pool);
                    counter++;
                    pool->liveNum++;
                }
            }
            pthread_mutex_unlock(&pool->mutexPool);
        }
        
        // 销毁线程
        // 忙的线程 × 2 < 存活的线程数 && 存活的线程数 > 最小线程数
        if (busyNum * 2 < liveNum && liveNum > pool->minNum) {
            pthread_mutex_lock(&pool->mutexPool);
            pool->exitNum = g_n;
            pthread_mutex_unlock(&pool->mutexPool);
            // 让工作的线程自己销毁
            for (int i = 0; i < g_n; i++) {
                pthread_cond_signal(&pool->notEmpty);
            }
        }
    }
    return NULL;
}


void threadExit(ThreadPool* pool) {
    pthread_t tid = pthread_self();
    for (int  i = 0; i < pool->maxNum; i++) {
        if (pool->threadIDs[i] == tid) {
            pool->threadIDs[i] = 0;
            printf("threadExit called, %ld exiting...\n", tid);
            break;
        }
    }
    pthread_exit(NULL);
}

void threadPoolAdd(ThreadPool* pool, void (*func)(void*), void* arg) {
    pthread_mutex_lock(&pool->mutexPool);
    while (pool->queueSize == pool->queueCapacity && !pool->shutdown) {
        // 阻塞生产者线程
        pthread_cond_wait(&pool->notFull, &pool->mutexPool);
    }
    if (pool->shutdown) {
        pthread_mutex_unlock(&pool->mutexPool);
        return;
    }
    // 添加任务
    pool->taskQ[pool->queueRear].function = func;
    pool->taskQ[pool->queueRear].arg = arg;
    pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;
    pool->queueSize++;

    // 唤醒阻塞在条件变量上工作的线程
    pthread_cond_signal(&pool->notEmpty);  
    // 解锁
    pthread_mutex_unlock(&pool->mutexPool);
}


int threadPooBusyNum(ThreadPool* pool) {
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);
    return busyNum;
}

int threadPoolLiveNum(ThreadPool* pool) {
    pthread_mutex_lock(&pool->mutexPool);
    int liveNum = pool->liveNum;
    pthread_mutex_unlock(&pool->mutexPool);
    return liveNum;
}

int threadPoolDestory(ThreadPool* pool) {
    printf("threadPoolDestory called...\n");
    if (pool == NULL) {
        return -1;
    }
    
    // 关闭线程池 设置里 shutdown = 1 ,线程池就会被自动销毁
    pool->shutdown = 1;
    // 阻塞回收管理者线程
    pthread_join(pool->managerID, NULL);
    // 唤醒阻塞的消费者线程
    for (int i = 0; i < pool->liveNum; i++) {
        pthread_cond_signal(&pool->notEmpty);
    }
    // 释放堆内存
    if (pool->taskQ) free(pool->taskQ);
    if (pool->threadIDs) free(pool->threadIDs);
    pthread_mutex_destroy(&pool->mutexPool);
    pthread_mutex_destroy(&pool->mutexBusy);
    pthread_cond_destroy(&pool->notEmpty);
    pthread_cond_destroy(&pool->notFull);
    free(pool);
    pool = NULL;
    return 0;
}

测试文件


#include "thread_pool.h"

#include <stdio.h>

void taskFunc(void* arg) {
    int* num = (int*)arg;
    printf("thread %ld is working, number = %d\n", pthread_self(), *num);
}

int main() {
    ThreadPool* pool = threadPoolCreate(3, 10, 100);
    for (int i = 0; i < 100; i++) {
        int* num = (int*)malloc(sizeof(int));
        *num = i + 100;
        threadPoolAdd(pool, taskFunc, num);
    }
    printf("100个任务添加完毕\n");
    sleep(30);
    threadPoolDestory(pool);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值