К Т П           План занятия                                                              1                                           Страницы  | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 |

6. Безопасное завершение потоков в Windows

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

Для иллюстрации этого подхода приведем пример программы, в которой выполняется разблокирование потоков. Прежде чем привести текст этой программы, кратко изложим суть ее работы. В рамках процесса выполняются два потока main и marker. Поток marker заполняет целыми числами элементы целочисленного массива a [size] при условии, что элемент массива является пустым. Мы предполагаем, что вначале все элементы массива a [size] являются пустыми и инициализированы нулями. Заполнение элементов происходит следующим образом: поток marker генерирует положительное случайное число и находит остаток от деления этого числа на size, а затем заполняет элемент, индекс которого равен этому остатку. В качестве заполнителя используется единица. Ясно, что когда-нибудь поток marker войдет в тупик, т. к. может сгенерировать индекс уже заполненного элемента. Задача потока main — обнаружить эток тупик и разблокировать поток marker. Разблокирование потока marker выполняется следующим образом: или потоку marker разрешается продолжить свою работу, или он должен безопасно завершить свою работу. Под безопасным завершением работы потока мы понимаем корректное освобождение всех захваченных этим потоком ресурсов И завершение ЭТОГО потока посредством вызова функции ExitThread. В листинге 8.1 приведен текст программы, выполняющей указанные действия.

Листинг 8.1. Восстановление работы процесса после обнаружения тупика

#include <windows.h>
#include <iostream.h>
#include <math.h>

const int size = 10; // размерность массива
int a[size]; // обрабатываемый массив
HANDLE hDeadlock; // сигнал о тупике
HANDLE hAnswer[2]; // для обработки тупика

DWORD WINAPI marker(LPVOID)
{
int i;
DWORD dwValue;

for (;;)
{
// вычисляем случайный индекс
i = abs(rand()) % size;
// проверяем, занят ли элемент
if (!a[i])
// нет, заполняем элемент
a[i] = 1;
else
{
// да, сигнализируем о тупике
SetEvent(hDeadlock);
// ждем ответа
dwValue = WaitForMultipleObjects(
2, hAnswer, FALSE, INFINITE);
if (dwValue == WAIT_FAILED)
{
cerr << "Wait function failed." << endl;
cerr << "Press any key to exit." << endl;
cin.get();

return GetLastError();
}
// вычисляем индекс сигнального объекта
dwValue -= WAIT_OBJECT_0;
switch (dwValue)
{
case 0: // продолжаем работу
continue;
case 1: // завершаем работу
ExitThread(1);
break;
default:
ExitThread(2);
break;
    }
   }
   }
  }

int main()
{
HANDLE hMarker;
DWORD idMarker;

// создаем событие, оповещающее о тупике
hDeadlock = CreateEvent(NULL, FALSE, FALSE, NULL);
// создаем события для обработки тупика
hAnswer[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
hAnswer[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
// запускаем поток marker
hMarker = CreateThread(NULL, 0, marker,NULL, 0, &idMarker);
if (hMarker == NULL)
return GetLastError();
for (;;)
{
char c;
// ждем сигнал о тупике
WaitForSingleObject(hDeadlock, INFINITE);
// выводим на консоль текущее состояние массива
cout << "Current state of the array: ";
for (int i = 0; i < size; ++i)
cout << a[i] << ' ';
cout << endl;
// завершать или нет поток marker?
cout << "Input 'y' to continue: ";
cin >> c;
if (c == 'y')
SetEvent(hAnswer[0]); // продолжаем работу
else
{
SetEvent(hAnswer[1]); // завершаем работу
break;
}
}

WaitForSingleObject(hMarker, INFINITE);
CloseHandle(hMarker);

return 0;
}

Обратим внимание на обработку в этой программе ситуации, когда возникает тупик. В этом случае мы используем для обработки тупика два события, которые сигнализируют потоку marker о завершении или продолжении работы. Второй момент касается заполнения элементов массива целыми числами. В нашем случае у нас фактически работает только один поток marker, а поток main просто ждет до тех пор, пока поток marker войдет в тупик. Поэтому для проверки значений элементов массива a [size] поток marker просто выполняет сравнения значений элементов этого массива с нулем. Если бы параллельно работало несколько потоков marker, то для выполнения этих действий пришлось бы использовать функцию InterlockedCompareExchange.


Предыдущая        В начало страницы       Следующая
6