Практическое руководство. Использование пула потоков (руководство по программированию на C#)

Обновлен: Ноябрь 2007

Пул потоков — это коллекция потоков, которые могут использоваться для выполнения нескольких задач в фоновом режиме. (Дополнительные сведения см. в разделе Использование потоков (Руководство по программированию на C#).) Это позволяет разгрузить главный поток для асинхронного выполнения других задач.

Пулы потоков часто используются в серверных приложениях. Каждый входящий запрос назначается потоку из пула, таким образом, запрос может обрабатываться асинхронно без задействования главного потока и задержки обработки последующих запросов.

Когда поток в пуле завершает выполнение задачи, он возвращается в очередь ожидания, в которой может быть повторно использован. Повторное использование позволяет приложениям избежать дополнительных затрат на создание новых потоков для каждой задачи.

Обычно пулы имеют максимальное количество потоков. Если все потоки заняты, дополнительные задачи помещаются в очередь, где хранятся до тех пор, пока не появятся свободные потоки.

Можно реализовать собственный пул потоков, но гораздо проще использовать пул, предоставляемый .NET Framework через класс ThreadPool.

В следующем примере пул потоков .NET Framework используется для вычисления результата Fibonacci для десяти чисел от 20 до 40. Каждый результат Fibonacci представляется классом Fibonacci, который предоставляет метод ThreadPoolCallback, выполняющий вычисление. Создается объект, представляющий каждое значение Fibonacci, и метод ThreadPoolCallback передается в элемент QueueUserWorkItem, который назначает выполнение метода доступному потоку из пула.

Поскольку каждому объекту Fibonacci назначается для вычисления полупроизвольное значение, и все потоки соревнуются за ресурсы процессора, невозможно заранее сказать. сколько времени понадобится на вычисление всех десяти значений. Именно поэтому каждый объект Fibonacci передается в экземпляр класса ManualResetEvent во время конструирования. Каждый объект сигнализирует соответствующему объекту события о завершении вычисления, что позволяет главному потоку блокировать выполнение WaitAll до завершения вычисления результата всех десяти объектов Fibonacci. После этого метод Main отображает все результаты Fibonacci.

Пример

using System;
using System.Threading;

public class Fibonacci
{
    public Fibonacci(int n, ManualResetEvent doneEvent)
    {
        _n = n;
        _doneEvent = doneEvent;
    }

    // Wrapper method for use with thread pool.
    public void ThreadPoolCallback(Object threadContext)
    {
        int threadIndex = (int)threadContext;
        Console.WriteLine("thread {0} started...", threadIndex);
        _fibOfN = Calculate(_n);
        Console.WriteLine("thread {0} result calculated...", threadIndex);
        _doneEvent.Set();
    }

    // Recursive method that calculates the Nth Fibonacci number.
    public int Calculate(int n)
    {
        if (n <= 1)
        {
            return n;
        }

        return Calculate(n - 1) + Calculate(n - 2);
    }

    public int N { get { return _n; } }
    private int _n;

    public int FibOfN { get { return _fibOfN; } }
    private int _fibOfN;

    private ManualResetEvent _doneEvent;
}

public class ThreadPoolExample
{
    static void Main()
    {
        const int FibonacciCalculations = 10;

        // One event is used for each Fibonacci object
        ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations];
        Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations];
        Random r = new Random();

        // Configure and launch threads using ThreadPool:
        Console.WriteLine("launching {0} tasks...", FibonacciCalculations);
        for (int i = 0; i < FibonacciCalculations; i++)
        {
            doneEvents[i] = new ManualResetEvent(false);
            Fibonacci f = new Fibonacci(r.Next(20,40), doneEvents[i]);
            fibArray[i] = f;
            ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
        }

        // Wait for all threads in pool to calculation...
        WaitHandle.WaitAll(doneEvents);
        Console.WriteLine("All calculations are complete.");

        // Display the results...
        for (int i= 0; i<FibonacciCalculations; i++)
        {
            Fibonacci f = fibArray[i];
            Console.WriteLine("Fibonacci({0}) = {1}", f.N, f.FibOfN);
        }
    }
}

Ниже приведены выходные данные.

launching 10 tasks...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
result calculated...
all calculations complete
Fibonacci(22) = 17711
Fibonacci(25) = 75025
Fibonacci(32) = 2178309
Fibonacci(36) = 14930352
Fibonacci(32) = 2178309
Fibonacci(26) = 121393
Fibonacci(35) = 9227465
Fibonacci(23) = 28657
Fibonacci(39) = 63245986
Fibonacci(22) = 17711

См. также

Задачи

Пример Monitor Synchronization Technology

Пример Wait Synchronization Technology

Основные понятия

Руководство по программированию в C#

Мониторы

Ссылки

Создание потоков (Руководство по программированию на C#)

Использование потоков (Руководство по программированию на C#)

Mutex

WaitAll

ManualResetEvent

Set

ThreadPool

QueueUserWorkItem

ManualResetEvent

Другие ресурсы

Практическое руководство. Синхронизация доступа к общим ресурсам в многопоточной среде с использованием Visual C# .NET

Безопасность в .NET Framework