线程池与性能

简介: 线程池与性能

1、为什么要使用线程池?

       某类任务非常耗时,严重影响该线程执行其他任务;任务主要分两类,一类是磁盘io,一类是网络io,在处理这些io任务的时候非常耗时,会长期占用cpu可能会影响其他任务的处理;

2、使用线程池原理

       在其他线程中异步处理耗时任务,当有耗时任务需要处理的时候,创建一个线程去异步执行这些耗时任务,但是开启线程数量和cpu核数有关,所以开启处理耗时任务的线程数量是有限的;需要在cpu核心之间做出平衡选择;从而通过创建和销毁线程来固定线程数量;但是创建和销毁线程非常消耗线程资源,所以将创建的线程放入线程池中来复用线程资源;充分利用系统资源来异步执行耗时任务;

3、线程池具体实现

       线程池是生产者消费者模式:生产者线程发布耗时任务,队列存放任务(有任务的上下文和任务的执行函数),任务的上下文本来实在生产者中执行的,现在要放在消费者线程中执行,线程池是一定线程数量的集合,消费者线程取出并执行任务,通过线程调度唤起休眠线程,唤起线程需要加锁mutex和条件变量condition;线程状态有从无到有及从有到无两种状态,通过条件变量唤醒线程或休眠线程,条件即使队列的状态,如果队列中有任务则唤醒线程,如果没有任务,则让线程睡眠,生产者发布任务后通知休眠线程唤醒来开展取出并执行任务的工作;

       线程需要平衡选择,平衡选择根据具体的耗时任务进行,耗时任务分为两种,io密集和cpu密集;io密集型会阻塞线程,cpu密集型则cpu长期被一个线程占用,不能处理其他任务;线程池中线程的个数如果是cpu密集型则和cpu核数相等,如果是io密集型则线程个数通常是2*cpu核数+2;

       如何确定线程池线程数量:

                               (io等待时间+cpu运算时间)*核数/cpu运算时间

4、实现线程池

接口设计:1)产生线程池接口,参数包括线程池数量,队列长度(因为线程的栈空间是固定的)

                   2)销毁线程池接口,参数包括标记线程池推出,通知所有线程

                   3)生产者抛出任务的接口,构造任务,放入队列,通知线程唤醒

                   4)线程池使用对象主要是生产者线程,是生产者线程使用线程池;

代码实现:

#ifndef _THREAD_POOL_H
#define _THREAD_POOL_H
typedef struct thread_pool_t thread_pool_t;
typedef void (*handler_pt) (void *);
thread_pool_t *thread_pool_create(int thrd_count, int queue_size);
int thread_pool_post(thread_pool_t *pool, handler_pt func, void *arg);
int thread_pool_destroy(thread_pool_t *pool);
int wait_all_done(thread_pool_t *pool);
#endif
#include <pthread.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "thrd_pool.h"
typedef struct task_t {
    handler_pt func;
    void * arg;
} task_t;
typedef struct task_queue_t {
    uint32_t head;
    uint32_t tail;
    uint32_t count;
    task_t *queue;
} task_queue_t;
struct thread_pool_t {
    pthread_mutex_t mutex;
    pthread_cond_t condition;
    pthread_t *threads;
    task_queue_t task_queue;
    int closed;
    int started; // 当前运行的线程数
    int thrd_count;
    int queue_size;
};
static void * thread_worker(void *thrd_pool);
static void thread_pool_free(thread_pool_t *pool);
thread_pool_t *
thread_pool_create(int thrd_count, int queue_size) {
    thread_pool_t *pool;
    if (thrd_count <= 0 || queue_size <= 0) {
        return NULL;
    }
    pool = (thread_pool_t*) malloc(sizeof(*pool));
    if (pool == NULL) {
        return NULL;
    }
    pool->thrd_count = 0;
    pool->queue_size = queue_size;
    pool->task_queue.head = 0;
    pool->task_queue.tail = 0;
    pool->task_queue.count = 0;
    pool->started = pool->closed = 0;
    pool->task_queue.queue = (task_t*)malloc(sizeof(task_t)*queue_size);
    if (pool->task_queue.queue == NULL) {
        // TODO: free pool
        return NULL;
    }
    pool->threads = (pthread_t*) malloc(sizeof(pthread_t) * thrd_count);
    if (pool->threads == NULL) {
        // TODO: free pool
        return NULL;
    }
    int i = 0;
    for (; i < thrd_count; i++) {
        if (pthread_create(&(pool->threads[i]), NULL, thread_worker, (void*)pool) != 0) {
            // TODO: free pool
            return NULL;
        }
        pool->thrd_count++;
        pool->started++;
    }
    return pool;
}
int
thread_pool_post(thread_pool_t *pool, handler_pt func, void *arg) {
    if (pool == NULL || func == NULL) {
        return -1;
    }
    task_queue_t *task_queue = &(pool->task_queue);
    if (pthread_mutex_lock(&(pool->mutex)) != 0) {
        return -2;
    }
    if (pool->closed) {
        pthread_mutex_unlock(&(pool->mutex));
        return -3;
    }
    if (task_queue->count == pool->queue_size) {
        pthread_mutex_unlock(&(pool->mutex));
        return -4;
    }
    task_queue->queue[task_queue->tail].func = func;
    task_queue->queue[task_queue->tail].arg = arg;
    task_queue->tail = (task_queue->tail + 1) % pool->queue_size;
    task_queue->count++;
    if (pthread_cond_signal(&(pool->condition)) != 0) {
        pthread_mutex_unlock(&(pool->mutex));
        return -5;
    }
    pthread_mutex_unlock(&(pool->mutex));
    return 0;
}
static void
thread_pool_free(thread_pool_t *pool) {
    if (pool == NULL || pool->started > 0) {
        return;
    }
    if (pool->threads) {
        free(pool->threads);
        pool->threads = NULL;
        pthread_mutex_lock(&(pool->mutex));
        pthread_mutex_destroy(&pool->mutex);
        pthread_cond_destroy(&pool->condition);
    }
    if (pool->task_queue.queue) {
        free(pool->task_queue.queue);
        pool->task_queue.queue = NULL;
    }
    free(pool);
}
int
wait_all_done(thread_pool_t *pool) {
    int i, ret=0;
    for (i=0; i < pool->thrd_count; i++) {
        if (pthread_join(pool->threads[i], NULL) != 0) {
            ret=1;
        }
    }
    return ret;
}
int
thread_pool_destroy(thread_pool_t *pool) {
    if (pool == NULL) {
        return -1;
    }
    if (pthread_mutex_lock(&(pool->mutex)) != 0) {
        return -2;
    }
    if (pool->closed) {
        thread_pool_free(pool);
        return -3;
    }
    pool->closed = 1;
    if (pthread_cond_broadcast(&(pool->condition)) != 0 ||
            pthread_mutex_unlock(&(pool->mutex)) != 0) {
        thread_pool_free(pool);
        return -4;
    }
    wait_all_done(pool);
    thread_pool_free(pool);
    return 0;
}
static void *
thread_worker(void *thrd_pool) {
    thread_pool_t *pool = (thread_pool_t*)thrd_pool;
    task_queue_t *que;
    task_t task;
    for (;;) {
        pthread_mutex_lock(&(pool->mutex));
        que = &pool->task_queue;
        // 虚假唤醒   linux  pthread_cond_signal
        // linux 可能被信号唤醒
        // 业务逻辑不严谨,被其他线程抢了该任务
        while (que->count == 0 && pool->closed == 0) {
            // pthread_mutex_unlock(&(pool->mutex))
            // 阻塞在 condition
            // ===================================
            // 解除阻塞
            // pthread_mutex_lock(&(pool->mutex));
            pthread_cond_wait(&(pool->condition), &(pool->mutex));
        }
        if (pool->closed == 1) break;
        task = que->queue[que->head];
        que->head = (que->head + 1) % pool->queue_size;
        que->count--;
        pthread_mutex_unlock(&(pool->mutex));
        (*(task.func))(task.arg);
    }
    pool->started--;
    pthread_mutex_unlock(&(pool->mutex));
    pthread_exit(NULL);
    return NULL;
}

               

5、reactor模型中使用线程池

可以用于处理decode,read,compute,encode,send任务,以为这些任务非常耗时;

6、nginx中的线程池

 

read读取出数据后,通过decode解码,由于compute的任务非常耗时,线程池可以用于执行compute任务;

目录
相关文章
|
4月前
|
缓存 负载均衡 安全
在Python中,如何使用多线程或多进程来提高程序的性能?
【2月更文挑战第17天】【2月更文挑战第50篇】在Python中,如何使用多线程或多进程来提高程序的性能?
54 4
|
24天前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
47 1
|
3月前
|
分布式计算 并行计算 安全
在Python Web开发中,Python的全局解释器锁(Global Interpreter Lock,简称GIL)是一个核心概念,它直接影响了Python程序在多线程环境下的执行效率和性能表现
【6月更文挑战第30天】Python的GIL是CPython中的全局锁,限制了多线程并行执行,尤其是在多核CPU上。GIL确保同一时间仅有一个线程执行Python字节码,导致CPU密集型任务时多线程无法充分利用多核,反而可能因上下文切换降低性能。然而,I/O密集型任务仍能受益于线程交替执行。为利用多核,开发者常选择多进程、异步IO或使用不受GIL限制的Python实现。在Web开发中,理解GIL对于优化并发性能至关重要。
52 0
|
3月前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
76 0
|
2月前
|
安全 云计算
云计算自旋锁问题之在线程安全地删除链表节点时,需要频繁加锁会影响性能如何解决
云计算自旋锁问题之在线程安全地删除链表节点时,需要频繁加锁会影响性能如何解决
37 2
|
4月前
|
NoSQL 数据处理 调度
【Redis深度专题】「踩坑技术提升」探索Redis 6.0为何必须启用多线程以提升性能与效率
【Redis深度专题】「踩坑技术提升」探索Redis 6.0为何必须启用多线程以提升性能与效率
352 0
|
2月前
|
大数据 API 数据处理
Python高手都在用的并发秘籍:解锁线程与进程的终极奥义,性能飙升不是梦!
【7月更文挑战第8天】Python并发编程提升性能,线程(threading)适合I/O密集型任务,如网络请求,通过`start()`和`join()`实现并发。进程(multiprocessing)利用多核CPU,适用于CPU密集型任务,如大数据处理。结合两者可优化混合任务,实现最佳并发效果。
29 1
|
3月前
|
安全 Java 调度
Java并发编程:优化多线程应用的性能与安全性
在当今软件开发中,多线程编程已成为不可或缺的一部分,尤其在Java应用程序中更是如此。本文探讨了Java中多线程编程的关键挑战和解决方案,重点介绍了如何通过合理的并发控制和优化策略来提升应用程序的性能和安全性,以及避免常见的并发问题。
45 1
|
2月前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
35 0
|
2月前
|
存储 安全 Java
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
26 0