И прежде чем обсуждать результаты его работы, понаблюдаем состояния (блокировки) его потоков командой
pidin
(с другого терминала, естественно). Вот несколько «снимков» состояний, здесь можно наблюдать весь спектр блокированных состояний, о которых говорилось выше:
5546027 1 ./n1 10r SIGSUSPEND
5546027 2 ./n1 10r NANOSLEEP
5546027 3 ./n1 10r NANOSLEEP
5546027 4 ./n1 10r SEND 5546027
5546027 5 ./n1 10r REPLY 5546027
5546027 6 ./n1 10r NANOSLEEP
5546027 7 ./n1 10r NANOSLEEP
5730347 1 ./n1 10r SIGSUSPEND
5730347 2 ./n1 10r RECEIVE 1
5730347 3 ./n1 10r NANOSLEEP
5730347 4 ./n1 10r NANOSLEEP
5730347 5 ./n1 10r NANOSLEEP
5730347 6 ./n1 10r NANOSLEEP
5730347 7 ./n1 10r NANOSLEEP
А
теперь рассмотрим результаты выполнения (на меньшем числе потоков клиентов, которое легче анализировать):
# n1 3
3: [0000000]->[0000000] ... [0000501]->[0000501]
4: [0000000]->[0000501] ... [0001003]->[0001003]
5: [0000000]->[0001003] ... [0001505]->[0001505]
3: [0002003]->[0002003] ... [0002504]->[0002505]
5: [0003462]->[0003462] ... [0003964]->[0003964]
4: [0003485]->[0003964] ... [0004466]->[0004466]
3: [0005017]->[0005017] ... [0005518]->[0005518]
5: [0005624]->[0005624] ... [0006126]->[0006126]
4: [0006741]->[0006741] ... [0007243]->[0007243]
...
Видно, как 3 клиента отправляют сообщения одновременно ([
0000000
]), поток сервера (TID = 2) немедленно получает сообщение ([
0000000
], 1-я строка), отправленное клиентом с TID = 3, два других сообщения (от клиентов с TID = 4 и 5) помещаются системой в очередь обслуживания (строки 2 и 3). После завершения обслуживания запроса от TID = 3 и ответа ([
0000501
]) поток сервера получает (извлекается из очереди ранее отправленное сообщение) сообщение от TID = 4 и так далее.
Еще содержательнее для интерпретации становится картина для большего числа потоков клиентов (здесь очередь ожидающих запросов становится гораздо длиннее, а ее поведение трудно предсказуемым - почти каждый запрос ожидает обслуживания), но эти результаты требуют намного более тщательного разбора для их осмысления:
# n1 10
3: [0000000]->[0000000] ... [0000501]->[0000501]
4: [0000000]->[0000501] ... [0001003]->[0001003]
5: [0000000]->[0001003] ... [0001505]->[0001505]
6: [0000000]->[0001505] ... [0002007]->[0002007]
7: [0000000]->[0002007] ... [0002508]->[0002508]
8: [0000000]->[0002508] ... [0003010]->[0003010]
9: [0000000]->[0003010] ... [0003512]->[0003512]
10: [0000000]->[0003512] ... [0004014]->[0004014]
11: [0000000]->[0004014] ... [0004516]->[0004516]
12: [0000000]->[0004516] ... [0005017]->[0005018]
3: [0005501]->[0005501] ... [0006003]->[0006003]
5: [0008024]->[0008024] ... [0008526]->[0008526]
7: [0008038]->[0008526] ... [0009028]->[0009028]
4: [0009273]->[0009273] ... [0009775]->[0009775]
6: [0010377]->[0010377] ... [0010878]->[0010878]
8: [0010590]->[0010878] ... [0011380]->[0011380]
9: [0010952]->[0011380] ... [0011882]->[0011882]
12: [0011297]->[0011882] ... [0012384]->[0012384]
11: [0011356]->[0012384] ... [0012886]->[0012886]
10: [0012024]->[0012886] ... [0013387]->[0013388]
3: [0012874]->[0013388] ... [0013889]->[0013889]
7: [0014888]->[0014888] ... [0015390]->[0015390]
4: [0016254]->[0016254] ... [0016756]->[0016756]
5: [0017646]->[0017646] ... [0018148]->[0018148]
6: [0019088]->[0019088] ... [0019590]->[0019590]
11: [0020206]->[0020206] ... [0020708]->[0020708]
8: [0020320]->[0020708] ... [0021210]->[0021210]
10: [0021078]->[0021210] ... [0021712]->[0021712]
12: [0021384]->[0021712] ... [0022213]->[0022213]
7: [0021630]->[0022213] ... [0022715]->[0022715]
9: [0021811]->[0022715] ... [0023217]->[0023217]
3: [0022009]->[0023217] ... [0023719]->[0023719]
Динамический
пул потоков
Динамический пул потоков не является каким-то специфическим механизмом, продиктованным именно микроядерной архитектурой QNX. Это удачная искусственная конструкция, все определения которой размещены в файле
<sys/dispatch.h>
. Удивительно не то, что в составе API QNX имеется такой механизм, а то, что подобные инструменты отсутствуют в других ОС.
В предыдущих примерах кода мы неоднократно создавали наборы потоков для тех или иных целей, но всем им было присуще одно: общее количество потоков в них было фиксированным на момент создания. Это и были статическиепулы потоков, разделяющих между собой работу приложения. Архитекторы QNX идут чуть дальше: они предоставляют инструментарий для создания пулов однотипных(с общей функцией потока) потоков, в которых конкретное число потоков может увеличиваться или уменьшаться синхронно с изменением нагрузки на приложение. Именно своим динамическимсоставом эта конструкция и отличается.