Linux线程

目录

1 线程

1.1 线程控制原语

1.1.1 pthread_self和pthread_create

1.1.2 循环创建多个线程

1.1.3 线程退出

1.1.4 pthread_join函数

1.1.5 获取线程函数的返回值

1.1.6 线程分离

1.1.7 杀死线程

1.1.8 进程和线程控制原语对比

1.2 线程属性

1.2.1 线程属性相关函数

1.2.2 线程的分离状态

1.2.3 线程的栈大小

2 线程同步

2.1 互斥量mutex

2.1.1 死锁

2.1.2 pthread_mutexattr_t

2.2 读写锁

2.2.1 相关函数

2.3 条件变量

2.3.1 相关函数

2.3.2 生产者消费者模型

2.4 信号量——同步

2.4.1 信号量相关函数

3 线程池

3.1 线程池代码


 


1、线程概念

线程与进程的关系

线程之间的共享和独占资源

优缺点

2、线程控制原语

pthread_self

pthread_create

exit

join

3、线程属性

4、使用线程时的注意事项

1 线程

线程:LWP(light-weight process) 轻量级的进程,本质仍是进程

线程也具有控制块Thread Control Block

线程是最小的执行(获得时间片即可执行)单位,进程是最小的资源(内存空间,硬件资源等)分配单位——当开启了线程后,CPU分配时间片的单位就变成了线程。此时开了多个线程的进程获得时间片的概率更高,占比更大。

从内核来看进程和线程是一样的,因为内核区分执行程序的方式是PCB,线程和进程有不同的PCB,但是PCB中指向的三级页表是相同的。

PCB中的一个指针,指向一个页表(4KB),页表中存在一条一条的目录项,目录项中也存在一个指针,指针指向二级页表,页表中的目录项的指针指向物理页面。

每一个函数有属于自己的栈帧空间,栈中存放着函数需要的变量。

所以对于线程,它的栈空间是独立的。

使用ps -Lf pid,可以查看一个进程都有哪些线程,LWP表示线程号(CPU分配时间片的依据)。线程ID是进程内区分不同线程的依据。线程号与线程ID不同。

线程资源

1、线程共享资源

(1)文件描述符表

(2)每种信号的处理方式

(3)当前工作目录

(4)用户ID和组ID

(5)内存地址空间

2、线程非共享资源

(1)线程ID

(2)处理器现场(寄存器)和栈指针

(3)独立的栈空间

(4)errno变量

(5)信号屏蔽字

(6)调度优先级

线程优缺点

1、优点

(1)提高程序的并发性

(2)相对于进程,开销小

(3)数据通信、共享数据方便

2、缺点

(1)线程中的函数大多都是库函数,不稳定

(2)调试、编写困难、gdb不支持

(3)对信号支持不好

线程库属于第三方库,在链接时会链接标准C库,但不会主动链接第三方库,所以gcc编译时要加上——" -pthread "选项。

1.1 线程控制原语

1.1.1 pthread_self和pthread_create

#include

pthread_self——获取线程ID。

pthread_t pthread_self(void);   //返回值

pthread_create——创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                               void *(*start_routine)(void *), void *arg);

成功,返回0;失败,返回错误代码

char *strerror(int errnum);  //接受一个错误号,返回错误描述

示例

主线程打印线程ID,进程ID;子线程打印线程ID,进程ID。主线程sleep等待子进程结束。

Linux线程_第1张图片

makefile

Linux线程_第2张图片

执行结果

Linux线程_第3张图片

1.1.2 循环创建多个线程

示例

循环创建5个子线程,并在线程中打印是第几个子线程

Linux线程_第4张图片

执行结果

Linux线程_第5张图片

注意:

画红线部分,使用的是传指针,传指针时使用的值是指针指向的i的值,但是由于线程具有并发性,并不能保证每一条线程都是顺序执行的,且一定比主线程先结束,所以此时获得的i可能已经不是实时的i值了。此时的执行结果如下图所示

Linux线程_第6张图片

1.1.3 线程退出

void pthread_exit(void *retval);  //退出当前线程,在主线程中调用则退出主线程

在主线程中执行pthread_exit退出而不是使用exit或return时(exit和return是退出进程),此时进程会在其他线程执行完后结束

在编写线程时,注意exit,return的使用,尽量使用pthread_exit

1.1.4 pthread_join函数

阻塞等待线程退出,获取线程退出状态

int pthread_join(pthread_t thread, void **retval);  //retval回收pthread_exit的参数

回收多个线程

循环调用pthread_join()函数

1.1.5 获取线程函数的返回值

pthread_exit(),可以返回线程函数的返回值

pthread_join(),可以获得pthread_exit()的参数

这两个函数配合,即可获得线程的返回值

也可以使用线程创建时的参数来获得线程的返回值??

1.1.6 线程分离

int pthread_deatch(pthread_t thread);  //成功,返回0;失败,返回错误码

线程分离状态:线程主动与主线程断开关系。线程结束后,其退出状态不由其他线程获取,而是直接自己自动释放。

    一般情况下,线程终止后,其终止状态一直保留到其他线程调用pthread_join获取它的状态为止。但是线程被置为detach状态后,线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个处于detach状态的线程调用pthread_join。

1.1.7 杀死线程

int pthread_cancel(pthread_t thread); //成功,返回0;失败,返回错误号

杀死线程需要一个取消点(在线程中调用系统调用即可),必须是系统调用——比如系统调用中的create,write等。

如果没有取消点,可以直接在线程中使用pthread_testcancel函数——void pthread_testcancel(void)

1.1.8 进程和线程控制原语对比

进程 线程
fork pthread_create
exit pthread_exit
wait pthread_join
kill pthread_cancel
getpid pthread_self

 

1.2 线程属性

pthread_attr

应先初始化线程属性,再pthread_create创建线程

1.2.1 线程属性相关函数

typedef struct 
{
    int etachstate;  //线程分离状态
    int schedpolicy;  //线程调度策略
    struct_sched_param schedparam;  //线程调度参数
    int inheritsched;  //线程继承性
    int scope;  //线程作用域
    size_t guardsize;  //线程栈末尾的警戒缓冲区大小
    int stackaddr_set;  //线程的栈设置
    void* stackaddr;  //线程栈的位置
    size_t stacksize;  //线程栈的大小
}pthread_attr_t;

 

初始化线程属性

    int pthread_attr_init(pthread_attr_t *attr);  //成功,返回0;失败,返回错误码

销毁线程属性所占用的资源

    int pthread_attr_destroy(pthread_attr *attr);  //成功,返回0;失败,返回错误号

1.2.2 线程的分离状态

设置线程属性——分离或非分离

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

获取线程属性

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);

分离状态:PTHREAD_CREATE_DETACHED

加入状态:PTHREAD_CREATE_JOINABLE

如果设置一个线程为分离线程,而这个线程又运行非常快,可以在线程中调用pathread_cond_timedwait函数,让线程等待一会,留出足够的时间让函数pthread_create返回。

1.2.3 线程的栈大小

当进程栈地址空间不够用时,指定新建线程使用由malloc分配的空间作为自己的栈空间。

int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);  //成功,返回0,;失败,返回错误码

int pthread_attr_getstack(pthread_attr_t *attr, void *stackaddr, size_t *stackszie);

2 线程同步

同步

互斥量——pthread_mutex

读写锁——pthread_rwlock

条件变量——pthread_cond;pthread_cond_wait()

信号量——互斥量的升级版

线程同步:一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其他线程为保证数据一致性,不能调用该功能。

2.1 互斥量mutex

每个线程对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁

1、pthread_mutex_init函数,pthread_mutex_destroy函数

#include 

int pthread_mutex_destroy(pthread_mutex_t *mutex);

//restrict,限制了修改的入口
int pthread_mutex_init(pthread_mutex_t *restrict mutex,   //restrict关键字,对该变量进行修改的操作,只能使用该变量操作(其他操作这个指针指向的变量的操作均非法)
        const pthread_mutexattr_t *restrict attr);  //属性的设置可以决定是否用于进程间互斥

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  //mutex的取值只有1和0,初始化成功值为1

 

2、pthread_mutex_lock函数,pthread_mutex_trylock函数,pthread_mutex_unlock函数

#include 
 
int pthread_mutex_lock(pthread_mutex_t *mutex);  //加锁,mutex--

int pthread_mutex_trylock(pthread_mutex_t *mutex);  //锁被占用时不阻塞

int pthread_mutex_unlock(pthread_mutex_t *mutex);  //解锁,mutex++

 

2.1.1 死锁

1、线程对同一互斥量加锁两次——阻塞自己导致死锁

2、线程之间的请求的资源被相互占用且不释放自己所占用的资源。线程之间形成一个逻辑上的环

死锁的解决方法

对于第一种,上锁和解锁要一一对应,且不允许嵌套

对于第二种,当无法请求到资源时,放弃自己已经占用的资源

2.1.2 pthread_mutexattr_t

互斥锁属性的设置

pthread_mutexattr_t

int pthread_mutexattr_init(pthread_mutexattr_t *attr);

int pthread_mutexattr_destroy(pthread_mutexattr_t *atr);

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); 

    pshared的取值:PTHREAD_PROCESS_PRIVATE(线程锁,进程间私有);PTHREAD_PROCESS_SHARED(进程锁)

2.2 读写锁

读写锁相对于互斥量具有更高的并行性。

特性:写独占,读共享;写锁优先级高

2.2.1 相关函数

#include 

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

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

1、pthread_rwlock_rdlock函数,pthread_rwlock_tryrdlock函数

#include 

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

2、pthread_rwlock_wrlock函数,pthread_rwlock_trywrlock函数

#include 

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

3、pthread_rwlock_unlock函数

#include 

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

 

 

2.3 条件变量

条件变量本身不是锁,但是它可以造成线程阻塞

条件变量通常与互斥锁配合使用

2.3.1 相关函数

1、pthread_cond_init函数,pthread_cond_destroy函数

#include 

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

int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_t cond= PTHREAD_COND_INITIALIZER;

2、pthread_cond_wait函数,pthread_cond_timedwait函数

(1)pthread_cond_wait

阻塞等待条件变量满足

释放已经掌握的互斥锁

被唤醒后,解除阻塞并重新申请获取互斥锁

(2)pthread_cond_timedwait

阻塞一段时间,在这段时间内尝试上锁

#include 

int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
                            pthread_mutex_t *restrict mutex,
                            const struct timespec *restrict abstime);

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

3、pthread_cond_signal函数,pthread_cond_broadcast

#include 

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);

 

2.3.2 生产者消费者模型

生产者,消费者,仓库

生产者:仓库未满,生产;仓库满了,阻塞等待

消费者:仓库为空,阻塞等待;仓库非空,消费

消费者:

(1)上锁

(2)检查条件变量是否满足——不满足,则wait会自动解锁

(3)执行消费者行为

(4)解锁

(4)唤醒生产者

生产者:

(1)上锁

(2)检查条件变量是否满足

(3)执行生产者行为

(4)解锁

(5)唤醒被阻塞的消费者

 

2.4 信号量——同步

由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁无法实现,只能将整个对象锁住,使得并发性下降

信号量,进化版的互斥锁(互斥锁mutex = 0 or 1,而信号量是1~N),即信号量表示空闲资源的数量。

2.4.1 信号量相关函数

1、sem_init函数,sem_destroy函数

#include 

//pshared:描述是否可以用于进程间共享
int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_destroy(sem_t *sem);

2、sem_wait函数,sem_trywait函数,sem_timedwait函数

#include 

int sem_wait(sem_t *sem);  //如果信号量大于0,则执行“ -- ”;等于0,则阻塞

int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

3、sem_post函数

#include 

int sem_post(sem_t *sem);  //信号量++,同时唤醒阻塞在信号量上的线程

 

 

3 线程池

来了一个事件,放入任何队列

线程从任务队列中取任务进行处理——无任务时,线程阻塞

这里类似生产者,消费者模型

线程池有一个基数,当任务超过基数时,扩大线程池容量,所以需要设置要给最小线程数,最大线程数,扩容步长

Linux线程_第7张图片

3.1 线程池代码

1、如何创建线程池——创建好线程后,由于没有任务,要把线程阻塞(条件变量)。线程的创建需要处理函数,而实际的任务是多样性的,多以任务本身并不是创建线程所使用的函数,而是在线程中调用任务函数。相当于加上了一层包装,包装使得内部透明,不知细节。

2、如何向线程池中添加任务——

3、参数如何传递到线程中——一个全局变量即可完成参数的共享,即创建一个全局任务队列,队列中存放着任务以及参数。通过传指针的方式便可以访问该全局变量,当然对于共享变量,要上锁。

4、

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "threadpool.h"

#define DEFAULT_TIME 10  /*10s检测一次*/
#define MIN_WAIT_TASK_NUM 10  /*如果队列 > MIN_WAIT_TASK_NUM,则扩容线程池*/
#define DEFAULT_THREAD_VARY 10  /*每次创建和销毁的线程个数*/

#define true 1
#define false 0


typedef struct {
    void *(*function)(void*);  /*函数指针,回调函数*/
    void *arg;  /*回调函数参数*/
}threadpool_task_t;  /*各子线程任务结构体*/

struct threadpool_t {
    pthread_mutex_t lock;  /*用于锁住本结构体*/
    pthread_mutex_t thread_counter;  /*记录忙状态线程个数的锁*/
    pthread_cond_t queue_not_full;  /*当任务队列满时,添加任务的线程阻塞,等待此条件变量*/
    pthread_cond_t queue_not_empty;  /*任务队列不为空时,通知等待任务的线程*/

    pthread_t *threads;  /*存放线程池中每个线程的tid*/
    pthread_t adjust_tid;  /*存管理线程的tid*/
    threadpool_task_t *task_queue;  /*任务队列*/

    int min_thr_num;  /*线程池最小线程数*/
    int max_thr_num;  /*线程池最大线程数*/
    int live_thr_num;  /*当前存活线程个数*/
    int busy_thr_num;  /*忙状态线程个数*/
    int wait_exit_thr_num;  /*要销毁的线程个数*/

    int queue_front;  /*task_queue队头下标*/
    int queue_rear;  /*task_queue队尾下标*/
    int queue_size;  /*task_queue队中实际任务数*/
    int queue_max_size;  /*task_queue队列中可容纳任务数上限*/

    int shutdown;  /*标志位,线程池使用状态,true或false*/
};


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*--------------------管理者线程--------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//
//扩容,缩容
void *adjust_thread(void *)
{
    int i;
    threadpool_t *pool = (threadpool_t *)threadpool;
    while (!pool->shutdown)
    {
        sleep(DEFAULT_TIME);  /*对线程池进行周期性管理*/
        
        pthread_mutex_lock(&(pool->lock));
        int queue_size = pool->queue_size;  /*任务数*/
        int live_thr_num = pool->live_thr_num;  /*存活线程数*/
        pthread_mutex_unlock(&(pool->lock));

        pthread_mutex_lock(&(pool->thread_counter));
        int busy_thr_num = pool->busy_thr_num;  /*忙线程数*/
        pthread_mutex_unlock(&(pool->thread_counter));

        /*创建新线程算法:任务数大于最小线程个数,且存活的线程数少于最大线程个数*/
        if (queue_size >= MIN_WAIT_TASK_NUM && live_thr_num < pool->max_thr_num)
        {
            pthread_mutex_lock(&(pool->lock));
            int add = 0;
            
            /*一次增加 DEFAULT_THREAD 个线程*/
            for (int i = 0; i < pool->max_thr_num && add < DRFAULT_THREAD_VARY
                                && pool->live_thr_num < pool->max_thr_num; ++i)
            {
                if (pool->threads[i] == 0 || !is_thread_alive(pool->threads[i]))
                {
                     pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void*)pool);
                     add++;
                     pthread_mutex_unlock(&(pool->lock));
                }

            }
            pthread_mutex_unlock(&(pool->lock));
        }
        
        /*销毁多余的空闲线程算法:忙线程 * 2 < 存活的线程数,且 存活的线程数 > 最小线程数*/
        if (busy_thr_num * 2 < live_thr_num && live_thr_num > pool->min_thr_num)
        {
            /*一次销毁 DEFAULT_THREAD 个线程*/  //这个本身合理吗??
            pthread_mutex_lock(&(pool->lock));
            pool->wait_exit_thr_num = DEFAULT_THREAD_VARY;
            pthread_mutex_unlock(&(pool->lock));
            
            for (i = 0; i < DEFAULT_THREAD_VARY; i++)  //
            { 
                /*尽管现在队列是空的,但依然发一个通知过去,让空闲线程不再阻塞,自动结束*/
                /*通知处在空闲状态的线程,它们会自行终止*/
                pthread_cond_signal(&(pool->queue_not_empty));
            }
        }
    }

    return NULL;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*--------------------工作线程----------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//
void *threadpool_thread(void *threadpool)
{
    threadpool_t *pool = (threadpool_t *)threadpool;
    threadpool_task_t task;

    while (true)
    {
        /*所以,没有任务的线程本身是阻塞的,参数如何传递进来呢?只要一个全局参数,就可以传递进来了*/
        /*如果有一个全局任务队列,任务都放在队列里,所有线程都可以访问该队列,就达到了目的*/
        /*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接受任务*/
        pthread_mutex_lock(&(pool->lock));  //锁住整个pool
        
        /*当任务队列为空,且线程池开启——让空闲线程自动终止*/
        while ((pool->queue_size == 0) && (!pool->shutdown))
        {
            printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());
            /*条件变量,等待任务队列不为空——当任务队列不为空时,发信号给阻塞线程*/
            pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));
            
            /*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
            /*让空闲线程主动终止*/
            if (pool->wait_exit_thr_num > 0)
            {
                pool->wait_exit_thr_num--;

                /*如果线程池里线程个数大于最小值时,可以结束当前线程*/
                if (pool->live_thr_num > pool->min_thr_num)
                {
                    printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
                    pool->live_thr_num--;
                    pthread_mutex_unlock(&(pool->lock));
                    pthread_exit(NULL);
                }
            }
        }

        /*关闭线程池,退出每一个线程*/
        if (pool->shutdown)
        {
            pthread_mutex_unlock(&(pool->lock));
            printf("thread 0X%X is exitiing\n", (unsigned int)pthread_self());
            pthread_exit(NULL);
        }
        
        /*从任务队列中获取任务*/
        task.function = pool->task_queue[pool->queue_front].function;
        task.arg = pool->task_queue[pool->queue_front].arg;
        
        pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;
        pool->queue_size--;

        /*通知可以有新的任务添加进来*/
        pthread_cond_broadcast(&(pool->queue_not_full));

        /*任务取出后,立即将线程池锁释放*/
        pthread_mutex_unlock(&(pool->lock));

        /*执行任务*/
        printf("thread ox%x start working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));  /*忙状态线程数变量锁*/
        pool->busy_thr_num++;
        pthread_mutex_unlock(&(pool->thread_counter));

        (*(task.function))(task.arg);  //执行回调函数

        
        /*任务结束处理*/
        printf("thread 0x%x end working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thr_num--;  /*忙线程状态数-1*/
        pthread_mutex_unlock(&(pool->thread_counter));
        
    }

    pthread_eixt(NULL);
}


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*----------------向队列添加任务------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//
int threadpool_add(threadpool_t *pool, void*(*function)(void *arg), void *arg)
{
    pthread_mutex_lock(&(pool->lock));  //锁住pool

    /*队列满了,调用wait阻塞*/
    while ((pool->queue_size == pool->queue_max_size) && (!pool->shutdown))
    {
        pthread_cond_wait(&(pool->queue_not_full), &(pool->lock));
    }
    if (pool->shutdown)
    {
        pthread_mutex_unlock(&(pool->lock));
    }

    /*清空工作线程调用的回调函数的参数arg*/
    /*为什么要清空队尾的数据*/
    /*队尾指针是指向空数据的*/
    if (pool->task_queue[pool->queue_rear].arg != NULL)
    {
        free(poll->task_queue[pool->queue_rear].arg);
        pool->task_queue[pool->queue_rear].arg = NULL;
    }

    /*添加任务到任务队列里*/
    pool->task_queue[pool->queue_rear].function = function;
    pool->task_queue[pool->queue_rear].arg = arg;
    pool->queue_rear = (pool->queue_rear + 1) % pool->queue_max_size;  /*环形队列,队尾指向空元素*/
    pool->queue_size++;

    /*添加完任务,队列不为空,唤醒线程池,等待处理任务的线程*/
    pthread_cond_signal(&(pool->queue_not_empty));
    pthread_mutex_unlock(&(pool->lock));
    
    return 0;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*--------------------创建线程池--------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//
threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
{
    int i;
    threadpool_t *pool = NULL;
    do {
        if ((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL)
        {
            printf("malloc threadpool fail\n");
            break;
        }
        
        pool->min_thr_num = min_thr_num;
        pool->max_thr_num = max_thr_num;
        pool->busy_thr_num = 0;
        pool->live_thr_num = min_thr_num;  /*初始时,活着的线程数*/
        pool->queue_size = 0;  /*初始时,有0个任务*/
        pool->queue_max_size = queue_max_size;
        pool->queue_front = 0;
        pool->queue_rear = 0;
        pool->shutdown = false;  /*初始时,不关闭线程池*/

        /*根据最大线程上限数,给工作线程数组开辟空间,并清零*/
        /*一个存储线程标识的数组*/
        pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * max_thr_num);
        if (pool->threads = NULL)
        {
            printf("malloc threads fail\n");
            break;
        }
        memset(pool->threads, 0, sizeof(pthread_t) * max_thr_num);

        /*队列开辟空间*/
        pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t) * queue_max_size);
        if (pool->task_queue == NULL)
        {
            printf("malloc task_queue fail\n");
            break;
        }
        
        /*初始化互斥锁,条件变量*/
        if (pthread_mutex_init(&(pool->lock), NULL) != 0
                || pthread_mutex_init(&(pool->thread_counter), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_empty), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_full), NULL) != 0)
        {
            printf("init the lock or cond fail\n");
            break;
        }

        /*启动min_thr_num个work thread*/
        /*创建了多个线程,线程函数是threadpool_thread*/
        for (i = 0; i < min_thr_num; ++i)
        {
            pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void*)pool); /*threadpool_thread,线程回调函数*/
            prinf("start thread 0x%x...\n", (unsigned int)pool->threads[i]);
        }

        /*启动管理者线程*/
        pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *)pool); /*启动管理者线程*/
        
        return pool;
    }while(0);

    threadpool_free(pool);  /*如果创建失败,则释放已经分配的内存*/
}


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*--------------------释放线程池空间----------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//

int threadpool_free(threadpool_t *pool)
{
    if (pool == NULL)
    {
        return -1;
    }

    if (pool->task_queue)
    {
        free(pool->task_queue);
    }

    if (pool->threads)
    {
        free(pool->threads);
        pthread_mutex_lock(&(pool->lock));
        pthread_mutex_destroy(&(pool->lock));
 
        pthread_mutex_lock(&(pool->thread_counter));
        pthread_mutex_destroy(&(pool->thread_counter));

        pthread_cond_destroy(&(pool->queue_not_empty));
        pthread_cond_destroy(&(pool->queue_not_full));

    }

    free(pool);
    pool = NULL;
 
    return 0;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*--------------------销毁线程池--------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//
int threadpool_destroy(threadpool_t *pool)
{
    int i = 0;
    if (pool == NULL)
    {
        return -1;
    }
    pool->shutdown = true;

    /*先销毁管理线程*/
    pthread_join(pool->adjust_tid, NULL);

    for (i = 0; i < pool->live_thr_num; ++i)
    {
        /*通知所有空闲线程*/
        pthread_cond_broadcast(&(pool->queue_not_empty));
    }

    for (i = 0; i < pool->live_thr_num; ++i)
    {
        pthread_join(pool->thread[i], NULL);  //等所有线程结束后,在结束销毁线程
    }

    threadpool_free(pool);
    
    return 0;
}


//+++++++++++++++++++++++++++++++++++++++++++++++++//
/*---------------判断线程是否存活-------------------*/
//+++++++++++++++++++++++++++++++++++++++++++++++++//

int is_thread_alive(pthread_t tid)
{
    int kill_rc = pthread_kill(tid, 0);  //发送0信号,测试线程是否存活
    if (kill_rc == ESRCH)
    {
        return false;
    }
    
    return true;
}

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(#,Linux)