Инверсия приоритетов
Наличие приоритетов может привести к неявной рокировке, когда более высокоприоритетный поток ждет сигнал от менее приоритетного потока.
- Поток T1 выполняет код в критической секции.
- Появляется поток T3 с наивысшим приоритетом в готовом состоянии поток T1 вытесняется.
- Поток T3, ожидая событие, отдает управление потоку T1, потому что в данный момент поток T1 наиболее приоритетный поток в готовом состоянии.
- Если в системе появляется готовый поток T2 в то время, как T1 не успел выйти из критической секции, то он (T2) блокирует более приоритетный поток T3, т.о. происходит инверсия приоритета.
Решение проблемы Win 2000 и Win 98:
В Win 2000 планировщик учитывает время простоя готовых потоков и случайным образом повышает их динамический приоритет.
В Win 98 диспетчер обнаруживает зависимости более приоритетного потока от менее приоритетного потока через объект ядра и повышает приоритет менее приоритетного потока до уровня приоритета более приоритетного потока.
Управление несколькими процессорами
|
|
Распределение потоков между процессорами.
- ThreadAffinity (соответствие потока определенному процессору). Указание данного свойства вынуждает поток исполняться на указанном подмножестве процессоров.
SetProcessAffinityMask() GetProcessAffinityMask()
SetThreadAffinityMask() GetThreadAffinityMask()
Установка аффинности может использоваться в двух случаях:
· Отладка многозадачного приложения (при отладке можно наблюдать за активностью каждой отдельной нити);
· Оптимизация под архитектуры с неоднородным доступом к памяти (NUMA).
Если зависимые нити поместить на один процессор, то они будут выполняться быстрее, так как будут обрабатываться через один внутренний КЭШ процессора, а не внешний, как в противном случае.
MSDN: назначение (жесткое) нитей на процессора может снизить производительность.
- Thread Ideal Processor (это не жесткая привязка, а “рекомендация” диспетчеру назначать поток на данный процессор).
SetIdealProcessor() GetProcessIdealProcessor()
Оптимизация в режиме пользователя
Указатели на функции/методы/классы.
- Указатель на функцию, возвращающую значение Int.
int (*f)(); – объявление указателя на функцию, возвращающую целое значение.
При этом: *f() – сама функция, f() – указатель.
На этот указатель выделяется память в статической области.
int *f(); – функция, возвращает указатель на int.
int f(int (*f1)()); – прототип функции, принимающей указатель на функцию в качестве аргумента.
Также можно записать следующим образом:
typedef int (*F1)();
int f(F1 f1); – прототип функции, принимающей в качестве аргумента f1 указатель на функцию (возвращающую целое значение), имеющий тип F1, описанный ранее оператором typedef.
|
|
- Обращение к методам:
Для вызова метода meth() объекта obj можно воспользоваться одной из следующих записей:
objmeth();
obj.meth();
(*obj).meth();
class Std_interface {
public: virtual void start() = 0;
virtual void suspend() = 0;
}
typedef void (Std_interface::* Pstd_mem)(); //Оператор:: – разрешение области
// видимости класса
void f(Std_interfece *p) {
Pstd_mem S = &Std_interface::suspend;//возвращает смещение до метода
//suspend() в таблице виртуальных
//методов Std_interface
(p*S)();
p suspend();
}
Данные указатели являются смещениями указателей в таблице Виртуальных методов.
Синхронизации в пользовательском режиме
Типы синхронизации:
- Совместное использование разделяемого ресурса (конкурентное взаимодействие);
- Уведомление потоков о завершении какой-либо операции (кооперативное взаимодействие).
Иллюстрация условия Гонки (race condition):
long g_x = 0;
DWORD WINAPI Thr1(PVOID) {
g_x++;
return 0;
}
DWORD WINAPI Thr2(PVOID) {
g_x++;
return 0;
}
LPTHREAD_START_ROUTINE thr_arr [2] = {Thr1, Thr2}
HANDLE trh_hnd [2];
Int main(…) {
DWORD id;
g_x = 0;
for (int i=0; i<2; i++) {
thr_hnd [i] = CreateThread (NULL, 0, thr_arr [i], NULL, 0, &id);
if (thr_hnd [i] = = NULL) ExitProcess(-1);
}
WaitForMultipleObjects(2, thr_hnd, TRUE, INFINITE)
}