Категории
Самые читаемые
💎Читать книги // БЕСПЛАТНО // 📱Online » Компьютеры и Интернет » Программирование » Системное программирование в среде Windows - Джонсон Харт

Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт

Читать онлайн Системное программирование в среде Windows - Джонсон Харт

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 80 81 82 83 84 85 86 87 88 ... 142
Перейти на страницу:

 r2cq_array = calloc(nthread, sizeof(queue_t));

 for (ithread = 0; ithread < nthread; ithread++) {

  /* Инициализировать очередь r2с для потока данного потребителя. */

  q_initialize(&r2cq_array[ithread], sizeof(msg_block_t), R2C_QLEN);

  /* Заполнить аргументы потока. */

  consumer_arg[ithread].thread_number = ithread;

  consumer_arg[ithread].work_goal = goal;

  consumer_arg[ithread].work_done = 0;

  consumer_th[ithread] = (HANDLE)_beginthreadex(NULL, 0, consumer, (PVOID)&consumer_arg[ithread], 0, &thid);

  producer_arg[ithread].thread_number = ithread;

  producer_arg[ithread].work_goal = goal;

  producer_arg[ithread].work_done = 0;

  producer_th[ithread] = (HANDLE)_beginthreadex(NULL, 0, producer, (PVOID)&producer_arg[ithread], 0, &thid);

 }

 transraitter_th = (HANDLE)_beginthreadex(NULL, 0, transmitter, NULL, 0, &thid);

 receiver_th = (HANDLE)_beginthreadex (NULL, 0, receiver, NULL, 0, &thid);

 _tprintf(_T("ХОЗЯИН: Выполняются все потокиn"));

 /* Ждать завершения потоков производителя. */

 for (ithread = 0; ithread < nthread; ithread++) {

  WaitForSingleObject(producer_th[ithread], INFINITE);

  _tprintf(_T("ХОЗЯИН: производитель %d выработал %d единичных сообщенийn"), ithread, producer_arg[ithread].work_done);

 }

 /* Производители завершили работу. */

 _tprintf(_T("ХОЗЯИН: Все потоки производителя выполнили свою работу.n"));

 /* Ждать завершения потоков потребителя. */

 for (ithread = 0; ithread < nthread; ithread++) {

  WaitForSingleObject(consumer_th[ithread], INFINITE);

  _tprintf(_T("ХОЗЯИН: потребитель %d принял %d одиночных сообщенийn"), ithread, consumer_arg[ithread].work_done);

 }

 _tprintf(_T("ХОЗЯИН: Все потоки потребителя выполнили свою работу.n"));

 ShutDown = 1; /* Установить флаг завершения работы. */

 /* Завершить выполнение и перейти в состояние ожидания передающих и принимающих потоков. */

 /* Эта процедура завершения работает нормально, поскольку и передающий,*/

 /* и принимающий потоки не владеют иными ресурсами, кроме мьютекса, */

 /* которые они могли бы покинуть по завершении выполнения, не уступив прав владения ими. Можете ли вы улучшить эту процедуру? */ 

 TerminateThread(transmitter_th, 0);

 TerminateThread(receiver_th, 0);

 WaitForSingleObject(transmitter_th, INFINITE);

 WaitForSingleObject(receiver_th, INFINITE);

 q_destroy(&p2tq);

 q_destroy(&t2rq);

 for (ithread = 0; ithread < nthread; ithread++) q_destroy(&r2cq_array [ithread]);

 free(r2cq_array);

 free(producer_th);

 free(consumer_th);

 free(producer_arg);

 free(consumer_arg);

 _tprintf(_T("Система завершила работу. Останов системыn"));

 return 0;

}

DWORD WINAPI producer(PVOID arg) {

 THARG * parg;

 DWORD ithread, tstatus;

 msg_block_t msg;

 parg = (THARG *)arg;

 ithread = parg->thread_number;

 while (parg->work_done < parg->work_goal) {

  /* Вырабатывать единичные сообщения, пока их общее количество */

  /* не станет равным "goal". */

  /* Сообщения снабжаются адресами отправителя и адресата, которые в */

  /* нашем примере одинаковы для всех сообщений, но в общем случае */

  /* могут быть различными. */

  delay_cpu(DELAY_COUNT * rand() / RAND_MAX);

  message_fill(&msg, ithread, ithread, parg->work_done);

  /* Поместить сообщение в очередь. */

  tstatus = q_put(&p2tq, &msg, sizeof(msg), INFINITE);

  parg->work_done++;

 }

 return 0;

}

DWORD WINAPI transmitter(PVOID arg) {

 /* Получись несколько сообщений от производителя, объединяя их в одно*/

 /* составное сообщение, предназначенное для принимающего потока. */

 DWORD tstatus, im;

 t2r_msg_t t2r_msg = {0};

 msg_block_t p2t_msg;

 while (!ShutDown) {

  t2r_msg.num_msgs = 0;

  /* Упаковать сообщения для передачи принимающему потоку. */

  for (im = 0; im < TBLOCK_SIZE; im++) {

   tstatus = q_get(&p2tq, &p2t_msg, sizeof(p2t_msg), INFINITE); 

   if (tstatus != 0) break;

   memcpy(&t2r_msg.messages[im], &p2t_msg, sizeof(p2t_msg));

   t2r_rasg.num_msgs++;

  }

  tstatus = q_put(&t2rq, &t2r_msg, sizeof(t2r_msg), INFINITE);

  if (tstatus != 0) return tstatus;

 }

 return 0;

}

DWORD WINAPI receiver(PVOID arg) {

 /* Получить составные сообщения от передающего потока; распаковать */

 /* их и передать соответствующему потребителю. */

 DWORD tstatus, im, ic;

 t2r_msg_t t2r_msg;

 msg_block_t r2c_msg;

 while (!ShutDown) {

  tstatus = q_get(&t2rq, &t2r_msg, sizeof(t2r_msg), INFINITE);

  if (tstatus != 0) return tstatus;

  /* Распределить сообщения между соответствующими потребителями. */

  for (im = 0; im < t2r_msg.num_msgs; im++) {

   memcpy(&r2c_msg, &t2r_msg.messages[im], sizeof(r2c_msg));

   ic = r2c_msg.destination; /* Конечный потребитель. */

   tstatus = q_put(&r2cq_array[ic], &r2c_msg, sizeof(r2c_msg), INFINITE);

   if (tstatus != 0) return tstatus;

  }

 }

 return 0;

}

DWORD WINAPI consumer(PVOID arg) {

 THARG * carg;

 DWORD tstatus, ithread;

 msg_block_t msg;

 queue_t *pr2cq;

 carg = (THARG *)arg;

 ithread = carg->thread_number;

 carg = (THARG *)arg;

 pr2cq = &r2cq_array[ithread];

 while (carg->work_done < carg->work_goal) {

  /* Получить и отобразить (необязательно — не показано) сообщения. */

  tstatus = q_get(pr2cq, &msg, sizeof(msg), INFINITE);

  if (tstatus != 0) return tstatus;

  carg->work_done++;

 }

 return 0;

Комментарии по поводу многоступенчатого конвейера

Данная реализация характеризуется некоторыми особенностями, суть которых частично отражена в комментариях, включенных в листинг программы. На эти же особенности обращают ваше внимание и упражнения 10.6, 10.7 и 10.10.

• Значительные возражения вызывает способ, используемый основным потоком для завершения выполнения передающего и принимающего потоков. Лучшим решением было бы использование конечных интервалов ожидания во внутренних циклах передатчика и приемника и прекращение работы после того, как будет установлен соответствующий глобальный флаг. Другой возможный подход заключается в отмене выполнения потоков, как описано далее в этой главе.

• Обратите внимание на существование симметрии между передающим и принимающим потоками. Как и при реализации очереди, это обстоятельство упрощает проектирование, отладку и сопровождение программы.

• Реализация не сбалансирована в смысле согласования скорости генерации сообщений, емкости конвейера и коэффициента блокирования "передатчик/приемник".

• В данной реализации (программа 10.4) для защиты очередей используются мьютексы. Результаты экспериментов с объектами CRITICAL_SECTION не позволили обнаружить сколько-нибудь заметного ускорения работы программы на однопроцессорной системе (см. приложение В). CS-версия программы, ThreeStageCS, находится на Web-сайте. Аналогичным образом вела себя программа и после того, как в ней была использована функции SignalObjectAndWait.

Асинхронные вызовы процедур

Основное возражение, которое можно предъявить к программе ThreeSage.c (программа 10.5) в ее нынешнем виде, касается прекращения выполнения передающего и принимающего потоков с помощью функции TerminateThread. В комментариях, включенных в код, вам предлагается подумать над более элегантным способом завершения выполнения потоков, который обеспечивал бы корректное прекращение работы программы и освобождение ресурсов.

Другой нерешенной проблемой является отсутствие общего метода (не считая использования функции TerminateThread), который обеспечивал бы отправку сигнала определенному потоку или инициировал его выполнение. События могут посылать сигналы одному потоку, ожидающему наступления автоматически сбрасываемого события, или всем потокам, ожидающим наступления вручную сбрасываемого события, но невозможно добиться того, чтобы сигнал был получен определенным потоком. Используемое до сих пор решение сводилось к тому, что пробуждались все ожидающие потоки, которые самостоятельно определяли, могут ли они теперь продолжить свое выполнение. Иногда привлекается альтернативное решение, суть которого состоит в назначении событий определенным потокам, так что сигнализирующий поток может определять, объект какого события следует перевести в сигнальное состояние одной из функций SetEvent или PulseEvent.

Обе эти проблемы решаются путем использования объектов асинхронного вызова процедур (Asynchronous Procedure Call, АРС). События развиваются в следующей последовательности, причем рабочий или целевой потоки должны управляться главным потоком.

• Главный поток указывает АРС-функцию данной целевого потока путем помещения объекта АРС в очередь АРС данного потока. В очередь могут быть помещены несколько АРС.

1 ... 80 81 82 83 84 85 86 87 88 ... 142
Перейти на страницу:
На этой странице вы можете бесплатно скачать Системное программирование в среде Windows - Джонсон Харт торрент бесплатно.
Комментарии