Прежде всего, мы попытаемся количественно оценить влияние объектов синхронизации на производительность, и сравним между собой объекты CRITICAL_SECTION и мьютексы. В программе statsMX.c (программа 9.1) для синхронизации доступа к специфической для каждого потока структуре данных используется мьютекс. Программа statsCS.c, листинг которой здесь не приводится, но его можно найти на Web-сайте книги, делает точно то же, но с использованием объекта CRITICAL_SECTION, тогда как в программе stats IN. с для этого привлекаются функции взаимоблокировки (interlocked functions). Наконец, в программе statsNS.с, которая также здесь не приводится, синхронизация вообще не используется; оказывается, в данном примере можно вообще обойтись без синхронизации, поскольку каждый рабочий поток обращается к собственной уникальной области памяти. Некоторые предостережения по этому поводу приведены в конце данного раздела. В реальных программах количество рабочих потоков может быть неограниченным, однако для простоты в программе 9.1 обеспечивается поддержка 64 потоков.
Описанная совокупность программ не только позволяет оценить зависимость производительности от выбора конкретного типа объекта синхронизации, но и говорит о следующих вещах:
• При тщательном проектировании программы в некоторых случаях можно вообще обойтись без использования синхронизации.
• В простейших ситуациях, например, когда требуется инкрементировать значение совместно используемой переменной, достаточно использовать функции взаимоблокировки.
• В большинстве случаев использование мьютексов обеспечивают более высокое быстродействие программы по сравнению с использованием объектов CS.
• Обычная методика заключается в определении структуры данных аргумента потока таким образом, чтобы она содержала информацию о состоянии, которая должна поддерживаться потоком, а также указатель на мьютекс или иной объект синхронизации.
/* Глава 9. statsMX.c */
/* Простая система "хозяин/рабочий", в которой каждый рабочий поток */
/* информирует главный поток о результатах своей работы для их отображения.*/
/* Версия, использующая мьютекс. */
#include "EvryThng.h"
#define DELAY_COUNT 20
/* Использование: statsMX nthread ntasks */
/* Запускается "nthread" рабочих потоков, каждой из которых поручается */
/* выполнение "ntasks" единичных рабочих заданий. Каждый поток сохраняет*/
/* информацию о выполненной работе в собственной неразделяемой ячейке */
/* массива, хранящего данные о выполненной потоком работе. */
DWORD WINAPI worker(void *);
typedef struct _THARG {
int thread_number;
HANDLE *phMutex;
unsigned int tasks_to_complete;
unsigned int *tasks_complete;
} THARG;
int _tmain(DWORD argc, LPTSTR argv[]) {
INT tstatus, nthread, ithread;
HANDLE *worker_t, hMutex;
unsigned int* task_count, tasks_per_thread;
THARG* thread_arg;
/* Создать мьютекс. */
hMutex = CreateMutex(NULL, FALSE, NULL);
nthread = _ttoi(argv[1]);
tasks_per_thread = _ttoi(argv[2]);
worker_t = malloc(nthread * sizeof(HANDLE));
task_count = calloc(nthread, sizeof(unsigned int));
thread_arg = calloc(nthread, sizeof(THARG));
for(ithread = 0; ithread < nthread; ithread++) {
/* Заполнить данными аргумент потока. */
thread_arg[ithread].thread_number = ithread;
thread_arg[ithread].tasks_to_complete = tasks_per_thread;
thread_arg[ithread].tasks_complete = &task_count[ithread];
thread_arg[ithread].phMutex = &hMutex
worker_t[ithread] = (HANDLE)_beginthreadex (NULL, 0, worker, &thread_arg[ithread], 0, &ThId);
}