.NET Custom Threadpool具有单独的实例

时间:2009-06-23 10:31:22

标签: c# web-crawler threadpool

什么是最推荐的.NET自定义线程池,它可以有单独的实例,即每个应用程序有多个线程池? 我需要一个不受限制的队列大小(构建一个爬虫),并且需要为我正在抓取的每个站点并行运行一个单独的线程池。

编辑: 我需要尽可能快地挖掘这些站点以获取信息,为每个站点使用单独的线程池将使我能够控制在任何给定时间在每个站点上工作的线程数。 (不超过2-3)

由于 Roey

6 个答案:

答案 0 :(得分:7)

我相信Smart Thread Pool可以做到这一点。它的ThreadPool类被实例化,因此您应该能够根据需要创建和管理单独的站点特定实例。

答案 1 :(得分:3)

Ami bar写了一个优秀的Smart线程池,可以实例化。

看看here

答案 2 :(得分:1)

问Jon Skeet:http://www.yoda.arachsys.com/csharp/miscutil/

如果你想要大量的并行运行任务,

Parallel extensions for .Net (TPL)实际上应该会更好。

答案 3 :(得分:1)

使用BlockingCollection可以用作线程的队列。 这是它的实现。 更新于2018-04-23:

public class WorkerPool<T> : IDisposable
{
    BlockingCollection<T> queue = new BlockingCollection<T>();
    List<Task> taskList;
    private CancellationTokenSource cancellationToken;
    int maxWorkers;
    private bool wasShutDown;

    int waitingUnits;

    public WorkerPool(CancellationTokenSource cancellationToken, int maxWorkers)
    {
        this.cancellationToken = cancellationToken;
        this.maxWorkers = maxWorkers;
        this.taskList = new List<Task>();
    }
    public void enqueue(T value)
    {
        queue.Add(value);
        waitingUnits++;
    }
    //call to signal that there are no more item
    public void CompleteAdding()
    {
        queue.CompleteAdding();          
    }

    //create workers and put then running
    public void startWorkers(Action<T> worker)
    {
        for (int i = 0; i < maxWorkers; i++)
        {
            taskList.Add(new Task(() =>
            {
                string myname = "worker " + Guid.NewGuid().ToString();

                try
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {                     
                        var value = queue.Take();
                        waitingUnits--;
                        worker(value);
                    }
                }
                catch (Exception ex) when (ex is InvalidOperationException)  //throw when collection is closed with  CompleteAdding method. No pretty way to do this.
                {
                    //do nothing
                }
            }));
        }

        foreach (var task in taskList)
        {
            task.Start();
        }
    }

    //wait for all workers to be finish their jobs
    public void await()
    {
        while (waitingUnits >0 || !queue.IsAddingCompleted)
            Thread.Sleep(100);

        shutdown();
    }

    private void shutdown()
    {
        wasShutDown = true;
        Task.WaitAll(taskList.ToArray());            
    }

    //case something bad happen dismiss all pending work
    public void Dispose()
    {
        if (!wasShutDown)
        {
            queue.CompleteAdding();
            shutdown();
        }
    }
}

然后像这样使用:

WorkerPool<int> workerPool = new WorkerPool<int>(new CancellationTokenSource(), 5);

workerPool.startWorkers(value =>
{
    log.Debug(value);
});
//enqueue all the work
for (int i = 0; i < 100; i++)
{
    workerPool.enqueue(i);
}
//Signal no more work
workerPool.CompleteAdding();

//wait all pending work to finish
workerPool.await();

您只需创建新的WorkPool对象就可以进行多次民意调查。

答案 4 :(得分:0)

这里的免费nuget库:CodeFluentRuntimeClient有一个可以重用的CustomThreadPool类。它是非常可配置的,您可以更改池线程的优先级,数量,COM单元状态,甚至名称(用于调试)以及文化。

答案 5 :(得分:0)

另一种方法是使用Dataflow Pipeline。我添加了这些稍后的答案,因为我发现数据流是解决此类问题(具有多个线程池的问题)的更好方法。它们提供了一种更加灵活和结构化的方法,并且可以轻松地垂直扩展。

您可以将代码分成一个或多个块,然后与Dataflows链接,然后让Dataflow引擎根据CPU和内存的可用性分配线程

我建议分为3个块,一个块用于准备对站点页面的查询,一个块用于访问站点页面,最后一个块用于分析数据。 这样,慢速块(获取)可能会分配更多线程来进行补偿。

这是数据流设置的样子:

var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };

prepareBlock.LinkTo(get, linkOptions);
getBlock.LinkTo(analiseBlock, linkOptions);

数据将从prepareBlock流到getBlock,然后流到analiseBlock。 块之间的接口可以是任何类,只需要相同即可。请参见Dataflow Pipeline

上的完整示例

使用数据流将是这样的:

 while ...{
    ...
    prepareBlock.Post(...); //to send data to the pipeline
 }
 prepareBlock.Complete(); //when done
 analiseBlock.Completion.Wait(cancellationTokenSource.Token); //to wait for all queues to empty or cancel