Thread->线程池

本文介绍了线程池的基本概念,包括Callable与Future的工作原理,Executors提供的多种线程池创建方式及其应用场景,并展示了如何使用ScheduledExecutorService进行定时任务的管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言Callable与Future


在介绍线程池前,我们先介绍下Callable与Future因为等会封装异步任务会用到.而异步任务Runnable相信都在熟悉不过了,Callable与Runnable类似,但Callable有返回值.

public interface Callable<V> {

    V call() throws Exception;
}

类型参数就是返回值类型,比如Callable<Integer>表示一个返回值类型为Integer的Callable.

至于异步任务Callable的执行结果会通过一个Future对象返回,Future接口具体有如下方法.

public interface Future<V> {
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
}
  • get()方法获取Callable执行结果,如果还未执行完将会阻塞直到可以获得结果,get(long timeout, TimeUnit unit)调用超时会抛出TimeoutException异常,如果运行该计算的线程被中断了,两个方法都将抛出InterruptedException.如果执行已经完成,那么get方法立即返回.

  • 如果callable还在执行,isDone()方法返回false,如果完成了返回true.

  • 可以用cancel取消计算,如果计算还没开始,它将被取消不在开始,如果计算已经运行中,那么mayInterruptIfRunning为true,它就被中断.

可以说Future里面不仅带着执行结果,并且如果你想中断当前计算也可以通过它.

使用的时候我们需要用FutureTask包装一下,FutureTask可以将Callable转换成Future和Runnable,它同时实现了二者的接口,下面我们来看一下如何使用

public class MyClass {

    public static void main(String[] args){

        FutureTask<Integer> task = new FutureTask<>(new MyCallable());
        new Thread(task).start();
        try {
            task.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class MyCallable implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            return 100;
        }
    }
}

使用非常简单这里就不在过多赘言.

Executors线程池


创建一个新的线程是有一定代价的,如果程序创建了大量生命周期很短的线程,就应该使用线程池.

执行器Executors有很多静态工厂方法可以构建线程池,具体如下

方法描述
newCachedThreadPool必要时创建新线程,空闲线程会被保留60秒
newFixedThreadPool创建一个定长线程池,空闲线程会被一直保留,可控制线程最大并发数,超出的线程会在队列中等待
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行
newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行.代替Timer
newSingleThreadScheduledeExecutor用于预定执行的单线程池

线程池

这里我们分两类介绍这些线程池,先看前三个.

  • newCachedThreadPool 方法构造的线程池,对于每个任务,如果有空闲线程可用,立即让它执行,如果没有空闲线程则创建新的线程.

  • newFixedThreadPool 方法构造一个具有固定大小的线程池,如果提交的任务数多于空闲的线程数,那么把得不到服务的任务放置在队列中.

  • newSingleThreadExecutor 是一个大小为1的线程池,由一个线程执行提交的任务.

这三个返回实现了ExecutorService接口的ThreadPoolExecutor类的对象.

我们可以通过ExecutorService接口的submit与execute方法执行异步任务.

void execute(Runnable command);

Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);

execute比较简单,这里我们说下submit方法.

  • 第一个submit方法返回一个Future<?>,可以使用这个对象调用isDone,cancel,或者isCancel.但是get方法在完成的时候只是返回null

  • 第二个submit方法在执行完成后返回一个指定的result对象.

  • 第三个submit提交一个Callable,并且返回对应的Future.

顺带说下它们的区别

  1. 接收的参数不同

  2. submit有返回值,而execute没有

  3. submit方便Exception处理,而execute处理异常比较麻烦.

当用完一个线程池的时候,调用shutdown,会使该线程池关闭,被关闭的线程池不在接受新的任务.当所有任务执行完了,线程池关闭.另一种是调用shutdownNow,该线程池取消还未开始的所有任务并试图中断还在运行的线程.

说了这么多来个例子看看

public static void main(String[] args){

    ExecutorService threadPool = Executors.newCachedThreadPool();//创建一个线程池
    Future<Integer> future = threadPool.submit(new MyCallable(1));//提交一个任务
    try {
        System.out.println(future.get());//获得执行结果
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    threadPool.shutdown();//关闭线程池
}

static class MyCallable implements Callable<Integer> {
    private int i;

    public MyCallable(int i) {
        this.i = i;
    }

    @Override
    public Integer call() throws Exception {
        Thread.sleep(i * 1000);
        return i;
    }
}

下面我们总结下使用线程池该做的

  1. 调用Executors类中的静态方法获取线程池

  2. 调用submit提交Runnable或Callable

  3. 如果想取消一个任务,或者想得到执行的结果那么需要保存好Future对象

  4. 当不在提交任何任务的时候,调用shutdown关闭线程池.

预定任务

接下来看另外两个线程池,Executors的newScheduledThreadPool和newSingleThreadScheduledeExecutor两个方法返回的实现了ScheduledExecutorService接口的对象,ScheduledExecutorService接口具有预定执行和重复执行的方法,可以预定执行Runnable或者Callable在初始延迟后运行一次,也可以周期性的执行一个Runnable.

ScheduledExecutorService常用方法如下

  • public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

    在预定时间后执行任务

  • public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

    在初始化延迟结束后,周期性的执行任务,周期执行间隔period.不受任务执行时间影响.

  • public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

    在初始化延迟结束后,周期性的执行给定任务,受任务执行时间影响,当第一次执行完后延迟delay后再执行第二次.

下面一个简单的例子,每隔两秒执行一次的定时任务.

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    @Override public void run() {
        //do something
    }
}, 0, 2000, TimeUnit.MILLISECONDS);

执行一组任务

前面我们已经介绍了如何使用线程池执行单个任务了,但有的时候需要执行一组任务,控制相关任务,这里我们介绍另外两组api.

<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

invokeAny方法提交一个Callable对象集合到线程池,并返回某个已经完成了的任务结果,无法知道究竟是哪个任务的结果.适用于比如要计算一个结果但是有很多种方式,但只要有一种方式计算出结果我们就不在需要计算了这种情况.

invokeAll方法提交一个Callable对象集合到线程池,返回一个Future对象列表,代表所有任务的结果.

到这里线程池基础就介绍完了,欢迎吹水.

多线程代码解释 typedef struct { pthread_t *threads; int count; atomic_int active_count; pthread_mutex_t mutex; pthread_cond_t cond; } ThreadPool; typedef struct { int client_sock; struct sockaddr_in client_addr; pthread_t thread_id; // 添加线程ID字段 } RequestContext; ThreadPool *thread_pool = NULL; /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /*! * @brief 创建线程池 * * @param[in] size 大小 * @return ThreadPool* */ ThreadPool *create_thread_pool(int size) { ThreadPool *pool = malloc(sizeof(ThreadPool)); if (!pool) { return NULL; } pool->threads = malloc(sizeof(pthread_t) * size); if (!pool->threads) { free(pool); return NULL; } pool->count = size; pool->active_count = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); for (int i = 0; i < size; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, pool) != 0) { perror("Failed to create thread"); destroy_thread_pool(pool); return NULL; } } return pool; } /*! * @brief 销毁线程池 * * @param[in] pool 线程池 */ void destroy_thread_pool(ThreadPool *pool) { if (!pool) { return; } for (int i = 0; i < pool->count; i++) { pthread_cancel(pool->threads[i]); } for (int i = 0; i < pool->count; i++) { pthread_join(pool->threads[i], NULL); } free(pool->threads); pthread_mutex_destroy(&pool->mutex); pthread_cond_destroy(&pool->cond); free(pool); } /*! * @brief 提交请求到线程池 * * @param[in] pool 线程池 * @param[in] ctx 请求 */ void submit_request_thread(ThreadPool *pool, RequestContext *ctx) { pthread_mutex_lock(&pool->mutex); while (atomic_load(&pool->active_count) >= pool->count) { pthread_cond_wait(&pool->cond, &pool->mutex); } atomic_fetch_add(&pool->active_count, 1); pthread_mutex_unlock(&pool->mutex); if (pthread_create(&ctx->thread_id, NULL, handle_request_thread, ctx) != 0) { perror("Failed to create request thread"); close(ctx->client_sock); free(ctx); pthread_mutex_lock(&pool->mutex); atomic_fetch_sub(&pool->active_count, 1); pthread_cond_signal(&pool->cond); pthread_mutex_unlock(&pool->mutex); } else { pthread_detach(ctx->thread_id); } } /*! * @brief 线程池工作线程的主函数 * * @param[in] arg 指向ThreadPool结构体的指针,包含线程池状态信息 * @return void* 线程退出状态(始终返回NULL) */ void *worker_thread(void *arg) { //ThreadPool *pool = (ThreadPool *)arg; while (1) { sleep(1); } return NULL; } /*! * @brief 线程请求处理函数 * * @param[in] arg 指向RequestContext结构体的指针,包含请求上下文信息 * @return void* 线程退出状态(始终返回NULL) */ void *handle_request_thread(void *arg) { RequestContext *ctx = (RequestContext *)arg; accept_request(ctx->client_sock); if (thread_pool) { pthread_mutex_lock(&thread_pool->mutex); atomic_fetch_sub(&thread_pool->active_count, 1); pthread_cond_signal(&thread_pool->cond); pthread_mutex_unlock(&thread_pool->mutex); } free(ctx); return NULL; } 详细解释上面代码的每一个单词的作用,
最新发布
08-16
<think>我们正在分析一个线程池中提交请求的函数。该函数将请求上下文提交到线程池,如果当前活动线程数已达到线程池的总线程数,则等待直到有空闲线程。然后增加活动线程计数,并创建新线程处理请求。如果创建线程失败,则进行错误处理(关闭客户端套接字,释放上下文,并减少活动线程计数并发出信号)。如果成功,则分离线程(使其在结束时自动回收资源)。 注意:这里使用了原子操作来更新活动线程计数,并使用条件变量进行等待和通知。 我们将为函数添加注释,并解释关键代码。</think>以下是对 `submit_request_thread` 函数的完整注释和代码解释: ```c /*! * @brief 向线程池提交请求处理任务 * * 该函数将请求上下文提交到线程池,当有空闲线程时创建新线程处理请求。 * 实现线程数量限制、资源管理和错误处理机制。 * * @param[in] pool 线程池实例指针 * @param[in] ctx 请求上下文指针(包含客户端socket等信息) */ void submit_request_thread(ThreadPool *pool, RequestContext *ctx) { // 获取线程池互斥锁 pthread_mutex_lock(&pool->mutex); // 等待直到有空闲线程可用 while (atomic_load(&pool->active_count) >= pool->count) { // 条件变量等待(自动释放锁,唤醒时重新获取) pthread_cond_wait(&pool->cond, &pool->mutex); } // 增加活动线程计数(原子操作) atomic_fetch_add(&pool->active_count, 1); // 释放互斥锁 pthread_mutex_unlock(&pool->mutex); // 创建新线程处理请求 if (pthread_create(&ctx->thread_id, NULL, handle_request_thread, ctx) != 0) { // 线程创建失败处理 perror("Failed to create request thread"); // 关闭客户端socket close(ctx->client_sock); // 释放请求上下文内存 free(ctx); // 更新线程池状态(加锁保护) pthread_mutex_lock(&pool->mutex); atomic_fetch_sub(&pool->active_count, 1); // 减少活动计数 pthread_cond_signal(&pool->cond); // 通知等待线程 pthread_mutex_unlock(&pool->mutex); } else { // 线程创建成功,设置为分离状态(自动回收资源) pthread_detach(ctx->thread_id); } } ``` ### 关键代码解析: 1. **线程资源管理**: ```c while (atomic_load(&pool->active_count) >= pool->count) { pthread_cond_wait(&pool->cond, &pool->mutex); } ``` - 使用条件变量等待空闲线程 - 避免忙等待(busy-waiting),节省CPU资源 - 遵循标准的等待条件模式 2. **原子计数操作**: ```c atomic_fetch_add(&pool->active_count, 1); // 增加计数 atomic_fetch_sub(&pool->active_count, 1); // 减少计数 ``` - 使用原子操作确保线程安全 - 避免使用互斥锁保护简单计数器 3. **线程生命周期管理**: ```c pthread_create(&ctx->thread_id, NULL, handle_request_thread, ctx); pthread_detach(ctx->thread_id); // 设置分离状态 ``` - 线程创建后立即分离,避免需要`pthread_join` - 分离线程结束后自动释放资源 4. **错误处理机制**: ```c if (pthread_create(...) != 0) { perror("..."); close(ctx->client_sock); free(ctx); // 更新线程池状态... } ``` - 完整处理线程创建失败场景 - 释放所有相关资源(socket、内存) - 恢复线程池状态 ### 典型线程池结构定义: ```c typedef struct { pthread_mutex_t mutex; // 互斥锁 pthread_cond_t cond; // 条件变量 atomic_int active_count; // 当前活动线程数 size_t count; // 最大线程数 // 其他管理字段... } ThreadPool; typedef struct { int client_sock; // 客户端socket pthread_t thread_id; // 线程ID struct sockaddr_in client_addr; // 客户端地址 // 其他请求相关数据... } RequestContext; ``` ### 性能优化建议: 1. **线程池预热**: ```c // 初始化时创建空闲线程 void init_thread_pool(ThreadPool *pool) { for (int i = 0; i < INITIAL_THREADS; i++) { pthread_t tid; pthread_create(&tid, NULL, idle_worker, pool); pthread_detach(tid); } } ``` 2. **动态线程扩展**: ```c // 当等待队列过长时动态创建线程 if (request_queue_size > pool->count * 2) { size_t new_count = pool->count + 5; if (new_count <= MAX_THREADS) { pool->count = new_count; } } ``` 3. **优雅关闭机制**: ```c // 设置关闭标志 atomic_store(&pool->shutdown, 1); // 唤醒所有等待线程 pthread_cond_broadcast(&pool->cond); ``` 4. **请求队列实现**: ```c // 添加请求队列支持 void submit_request_thread(ThreadPool *pool, RequestContext *ctx) { pthread_mutex_lock(&pool->mutex); enqueue(pool->queue, ctx); // 加入队列 pthread_cond_signal(&pool->cond); // 通知工作线程 pthread_mutex_unlock(&pool->mutex); } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值