Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharp_Prog_Guide.doc
Скачиваний:
16
Добавлен:
16.11.2019
Размер:
6.22 Mб
Скачать

Использование пула потоков

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

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

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

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

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

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

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

Example

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);

}

}

}

------

Here is the output:

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

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

----

Reflection

Reflection provides objects (of type Type) that describe assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, Reflection enables you to access them. For more information, see Attributes.

Here's a simple example of Reflection using the static method GetType - inherited by all types from the Object base class - to obtain the type of a variable:

// Using GetType to obtain type information:

int i = 42;

System.Type type = i.GetType();

System.Console.WriteLine(type);

The output is:

System.Int32

In this example, Reflection is used to obtain the full name of a loaded assembly:

// Using Reflection to get information from an Assembly:

System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");

System.Console.WriteLine(o.GetName());

The output is:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Note:

The C# keywords protected and internal have no meaning in IL and are not used in the Reflection APIs. The corresponding terms in IL are Family and Assembly. To identify an internal method using Reflection, use the IsAssembly property. To identify a protected internal method, use the IsFamilyOrAssembly.

Reflection Overview

Reflection is useful in the following situations:

  • When you have to access attributes in your program's metadata.

  • For examining and instantiating types in an assembly.

  • For building new types at runtime. Use classes in System.Reflection.Emit.

  • For performing late binding, accessing methods on types created at run time.