递归线程池队列

在开发爬虫时,为加快广度优先搜索的爬行速度,采用多线程并限制线程数。文章提出了递归线程池队列的概念,即线程数受限时,超出部分进入队列等待执行,线程中允许创建新线程。主要内容包括实现代码、线程调度和等待全部执行完毕的策略,以及在实现过程中如何处理递归导致的复杂性。

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

注意标题里面有三个keyword,递归 线程池 队列

这是我在开发爬虫程序时候碰到的需求,我们知道爬网页一般都使用广度优先搜索,每个页面下面都可能有下层页面。为了加快爬行速度,我们很容易想到用多线程来实现。

但是多线程必须受控,不能无限的创建线程,这样机器受不了,服务器也受不了。所以多线程必须满足下面条件:

线程数限制,超过限制则加入到队列中等待。同时,线程中允许创建新的线程,我称之为递归线程。

根据这个需求,所以有了标题:递归线程池队列

下面是实现代码

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace MediaSpider {
    class TaskQueue {
        int poolSize;
        Task[] pool;
        ConcurrentQueue<Action> queue;
        CancellationTokenSource cancelTokenSource=null;

        public TaskQueue(int poolSize) {
            this.poolSize = poolSize;
            // 初始化线程池
            pool = new Task[poolSize];
            for (int i = 0; i < poolSize; i++) {
                pool[i] = Task.Run(() => { });
            }
            queue = new ConcurrentQueue<Action>();
        }
        public void Add(Action action) {
            queue.Enqueue(action);
            if(cancelTokenSource!=null) cancelTokenSource.Cancel();
        }
        public void Wait() {
            while (true)
            {
                cancelTokenSource = null;
                int index = Task.WaitAny(pool);

                // 如果队列不为空,则加入线程池执行
                if (queue.TryDequeue(out Action action))
                {
                    pool[index] = Task.Run(action);
                    continue;
                }
                cancelTokenSource = new CancellationTokenSource();

                try
                {
                    // 等待线程池所有线程都执行完毕
                    Task.WaitAll(pool, cancelTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    // 如果队列追加,则执行队列的任务
                    continue;
                }
                // 如果都执行完毕,则退出等待
                return;
            }
        }

    }

}

调用方法

                pool = new TaskQueue(6);//初始化线程池,大小为6
                pool.Add(() => { Task.Delay(100); });//添加任务
                pool.Add(() => { Task.Delay(100); });//添加任务
                pool.Add(() => { Task.Delay(100); });//添加任务
                pool.Wait();//等待任务执行完毕

实现的思路如下:

主线程只是守护线程,任务都在其他线程里面实现。

守护线程(Wait)实现下面的功能

1.线程调度:当线程池有空闲的时候,把队列中的线程加入线程池执行

2.等待全部线程池和队列执行完毕


实现中遇到的问题

必须等待线程池和队列同时为空才能算执行完毕,线程也会不停的创建新的线程。

如果没有递归就比较好办,有了递归,队列都执行完了,线程池中的线程还可以添加队列。不能够单纯的WaitAll。想了几个方式,都有些问题。后来发现WaitAll(Task[], CancellationToken)是可以被打断的,才最终解决这个问题。

如果不需要递归,那么用普通的WaitAll就可以了,代码还可以简化。

任务队列刚开始是用Queue来实现的,由于不是线程安全需要Lock来互斥,后来发现线程安全的ConcurrentQueue,就把Lock去掉,提高了性能



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值