Процессы, потоки и нити в ОС Windows, страница 4

display("T2: Результат",callf1,callf2);

t2done=1;

}

void main()

{

index=TlsAlloc();

_beginthread(t_1,0,NULL);

_beginthread(t_2,0,NULL);

while (t1done==0||t2done==0);

TlsFree(index);

cout.flush();

MessageBox(NULL,"Done","Debug",MB_OK);

}

На рис. 3. показан результат работы программы.

Рис. 3. Работа программы TLS


Нити

Переключение между потоками осуществляется в соответствии с принципом приоритетности. Если система решает, что ваше время истекло, она «отбирает» центральный процессор у вашего потока и передает его в распоряжение другого потока. Однако в некоторых ситуациях может потребоваться более изощренный контроль над передачей управления между участками кода программы. Именно в этих случаях применяют нити (fibers).

Преобразовать поток в нить можно при помощи функции ConvertThreadToFiber. Каждый поток может обладать несколькими нитями. Когда система переключается с одного потока на выполнение другого, начинается выполнение текущей нити для данного потока. Если в потоке только одна нить, то вы, очевидно, не замените разницы — код, который раньше был частью потока, стал частью нити. Однако если в потоке несколько нитей, то вы можете управлять переключением между ними при помощи функции SwitchToFiber. Создать новую нить в текущем потоке можно при помощи функции CreateFiber.

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

Листинг 6. Использование нитей

#define _WIN32_WINNT 0x0400

#include <windows.h>

#undef MessageBox

#define MessageBox(s) MessageBoxA(NULL,s,"Fiber Demo",MB_OK)

void *fiber[4];

void fiber0(void *) // основная нить

{

            while (1)

            {

                        for (int i=1;i<4;i++)

                                   SwitchToFiber(fiber[i]);

            }

}

void CALLBACK fiber1(void *)

{

            while (1)

            {

                        MessageBox("Fiber 1");

                        SwitchToFiber(fiber[0]);

            }

}

void CALLBACK fiber2(void *)

{

            while (1)

            {

                        MessageBox("Fiber 2");

                        SwitchToFiber(fiber[0]);

            }

}

void CALLBACK fiber3(void *)

{

            while (1)

            {

                        MessageBox("Fiber 3");

                        SwitchToFiber(fiber[0]);

            }

}

void main()

{

            fiber[0]=ConvertThreadToFiber(NULL);

            fiber[1]=CreateFiber(0,fiber1,NULL);

            fiber[2]=CreateFiber(0,fiber2,NULL);

            fiber[3]=CreateFiber(0,fiber3,NULL);

            fiber0(NULL);

}

Результат работы программы показан на рис. 4.

Рис. 4. Работа программы с нитями


АРС

Модуль Windows Executive поддерживает несколько необычную технологию под названием Asynchronous Procedure Call (АРС). Эта технология позволяет одному потоку асинхронно инициировать выполнение некоторой функции в другом потоке. Иными словами, по команде первого потока выполнение второго потока асинхронно прерывается, второй поток выполняет некоторую функцию, после чего выполнение второго потока продолжается.

Исходный код простой программы, использующей QueueUserAPC, приведен в листинге 7. Функция main программы создает новый поток и на короткое время переходит в режим ожидания для того, чтобы поток успел начать работу. После этого программа обращается к функции QueueUserAPC. Теперь, как только созданный нами поток (tl) перейдет в «настороженное» состояние, он выполнит назначенную для него функцию АРС (apcfunc). В нашем примере поток переходит в «настороженное» состояние при помощи вызова SleepEx (но не Sleep). Однако, если основная программа не будет ждать, пока новый поток начнет работу, а сразу же, создав поток, поставит в его очередь функцию АРС, эта функция может быть выполнена в момент, когда поток создан, но не успел начать работу. Чтобы проверить это, закомментируйте вызов Sleep в функции main.