Потоки можно выбирать из множества потоков, управляемого классом ThreadPool. В нем существуют потоки двух типов: рабочие и потоки ввода-вывода. Мы можем управлять обоими количествами.
class Program
{
static void Main()
{
int nWorker, nAsyn;
ThreadPool.GetAvailableThreads(out nWorker, out nAsyn);
Console.WriteLine("\nWorker Threads: {0},\n"+"Async I/O Threads: {1}\n",nWorker,nAsyn);
ThreadPool.SetMaxThreads(20, 10);
ThreadPool.GetAvailableThreads(out nWorker, out nAsyn);
Console.WriteLine("\nWorker Threads: {0},\n"+"Async I/O Threads: {1}\n",nWorker,nAsyn);
}
}
MethodInvoker представляет делегатный тип, объект которого может выполнить любой метод с сигнатурой void Func(void); в рамках нового потока, выбранного из ThreadPool. Метод Func должен быть с пустым списком параметров и не принимають параметры.
Добавим в класс Program переменную count: счетчик запусков новых потоков.
static int count = 0;
Добавим процедуру, которая будет запускать метод Func в отдельном потоке.
void CallFuncAsyncTimes ()
{
MethodInvoker mi = new MethodInvoker(Func);
for (int i = 0; i < 30; i++)
mi.BeginInvoke(null, null); // mi.Invoke();
}
Пусть метод Func выводит результаты опроса объектов ThreadPool и Thread.
void Func ()
{
int nWorker, nAsyn;
ThreadPool.GetAvailableThreads(out nWorker, out nAsyn);
Thread thread = Thread.CurrentThread;
Console.WriteLine("{0, -4} {1, -3} {2} {3} {4}",
count++, thread.GetHashCode(), thread.IsThreadPoolThread, nWorker, nAsyn);
Thread.Sleep(30000);
}
В Main добавим вызов метода CallFuncAsyncTimes.
static void Main()
{
Program p = new Program();
ThreadPool.SetMaxThreads(20, 20);
Console.WriteLine("\nCallFuncAsyncTimes");
Console.WriteLine("# ID Pool Worker Left Async I/O Left\n");
p.CallFuncAsyncTimes();
Console.ReadKey();
}
Заметим, что:
¨ Все потоки выбираются из множества thread pool.
¨ При вызове Func, каждый поток получает свой ID.
¨ По исчерпании множества thread pool (после вызова 20-го потока) идентификаторы начинают повторяться. Потоки используются повторно.
¨ После вызова 20-го потока цикл for главного (primary) потока приложения ждет освобождения потока из множества pool.
¨ Как только появляется свободный поток, приложение тут же отдает его для выполнения метода Func.
Вывод: Не ныряйте очень глубоко в ThreadPool — может не хватить кислорода.
BeginInvoke запускает метод и не знает, когда он закончится. Метод EndInvoke блокирует приложение и ждет завершения потока. Заметим, что BeginInvokeвозвращает ссылку на объект, реализующий интерфейс IAsyncResult.
Этот интерфейс представляет стандарт определения статуса (результата) асинхронной процедуры и способов синхронизации процедур. Он реализован классами, в которых есть методы, умеющие выполняться асинхронно. Например, FileStream.BeginRead.
Интерфейс IAsyncResult определяет такие свойства:
¨ object AsyncState { get; } — Выдает определяемый пользователем объект, который содержит информацию о результате (или состоянии) асинхронной процедуры.
¨ bool IsCompleted { get; } — асинхронная процедура закончилась ?
¨ bool CompletedSynchronously { get; } — признак того, что асинхронная процедура завершилась синхронно. Это свойство может быть true для asynchronous I/O operation, если I/O-запрос был непродолжительным.
¨ WaitHandle AsyncWaitHandle { get; } — используется для ожидания завершения асинхронной процедуры. WaitHandle — обобщенный объект синхронизации ОС. Это базовый класс для классов, сигнализирующих о завершении процедуры, а точнее об освобождении ресурсов, занятых ею.
С помощью объекта, реализующего IAsyncResult, мы можем получить результат (или конечное состояние) асинхронной операции. Добавим метод FuncOne, который буде выполняться асинхронно.
void FuncOne() { Thread.Sleep(1000); }
Следующий метод UsingEndInvoke показывает, как получить результат с помощью свойства AsyncState. Оно присутствует в объекте, возвращаемом методом BeginInvoke, так как последний реализует интерфейс IAsyncResult.
void UsingEndInvoke()
{
MethodInvoker mi = new MethodInvoker(FuncOne);
Console.WriteLine("\nCalling BeginInvoke");
IAsyncResult res = mi.BeginInvoke(null, "My state");
mi.EndInvoke(res); // Program will block until FunсOne is complete!
string state = (string)res.AsyncState; // Once EndInvoke is complete, get the state object
Console.WriteLine("After Calling EndInvoke: " + state);
}
Заметим, что EndInvoke позволяет выловить исключения, выбрасываемые асинхронной процедурой (если поместить EndInvoke в блок try-catch). Если сделать то же самое с BeginInvoke, то исключение не поймать. Добавим код, имитирующий исключение.
void FuncOne() { Thread.Sleep(1000); throw new Exception("Wow! Something is wrong
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.