Изменить стиль страницы

  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  // восстановить приоритет целевой функции до уровня того,

  // кто ее устанавливал, вызывая конструктор

<b>  pthread_attr_setinheritsched(&amp;attr, PTHREAD_EXPLICIT_SCHED);</b>

<b>  pthread_attr_setschedparam(&amp;attr, &amp;p-&gt;param);</b>

  // запуск целевой функции в отдельном &quot;отсоединенном&quot; потоке

  pthread_create(NULL, &amp;attr, p-&gt;func, NULL);

  if (p-&gt;statistic()) ++p-&gt;sync;

 }

}

// 'пустой' обработчик сигнала SIGINT (реакция на ^С)

inline static void empty(int signo) {}

int main(int argc, char **argv) {

 // с этой точки стандартная реакция на ^С отменяется...

 signal(SIGINT, empty);

 // массив целевых функций

 void(*funcs[])(void) = { &amp;mon1, &amp;mon2, &amp;mon3 };

 // периоды их синхросерий запуска

 int period[] = { 317, 171, 77 };

 // приоритеты, на которых отрабатывается реакция

 // синхросерий на каждый из таймеров синхросерий

 int priority[] = { 15, 5, 25 };

 int num = sizeof(funcs) / sizeof(*funcs);

 // запуск 3-х синхронизированных последовательностей

 // выполнения (созданием объектов)

 thrblock** tb = new (thrblock*)[num];

 for (int i = 0; i &lt; num; i++) {

  tb[i] = new thrblock(funcs[i], period[i],

  priority[i], true);

  if (!tb[i]-&gt;OK())

  perror(&quot;synchro thread create&quot;), exit(EXIT_FAILURE);

 }

 // ... а теперь ожидаем ^С.

 pause();

 // подсчет статистики и завершение программы

 cout &lt;&lt; endl &lt;&lt; &quot;Monitoring finalisation!&quot; &lt;&lt; endl;

 // вывод временных интервалов будем делать в миллисекундах:

 const double n2m = 1000000.;

 for (int i = 0; i &lt; num, i++) {

  timestat *p = &amp;tb[i]-&gt;sync;

  !(*p); // подсчет статистики по объекту

  cout &lt;&lt; i &lt;&lt; '\t' &lt;&lt; p-&gt;num &lt;&lt; &quot;\t=&gt; &quot; &lt;&lt; p-&gt;mean / n2m &lt;&lt; &quot; [&quot; &lt;&lt;

   p-&gt;tmin / n2m &lt;&lt; &quot;...&quot; &lt;&lt; p-&gt;tmax / n2m &lt;&lt; &quot;]\t~&quot; &lt;&lt; p-&gt;disp / n2m &lt;&lt;

   &quot; (&quot; &lt;&lt; p-&gt;disp / p-&gt;mean * 100 &lt;&lt; &quot;%)&quot; &lt;&lt; endl;

 }

 return EXIT_SUCCESS;

}

Вся функциональность программы сосредоточена в одном классе —

thrblock
, который может в неизменном виде использоваться для разных приложений. Необычной особенностью объекта этого класса является то, что он выполнен в технике «активных объектов», навеянной поверхностным знакомством с языками программирования школы Н. Вирта — ActiveOberon и Zormon. В ней говорится, что конструктор такого объекта не только создает объект данных, но и запускает (как вариант) отдельный поток выполнения для каждого создаваемого объекта. В нашем случае задача потоковой функции состоит в вызове целевой функции, адрес которой был передан конструктору объекта в качестве одного из параметров. [26]

Ниже представлены отличия нашей реализации от простого цикла с задержкой, обсуждавшейся выше (помимо исправлений очевидных недостатков):

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

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

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

Запустим наше тестовое приложение:

# t3

+10+10*10+10+10.10*10+10+10*10+10+10.10*10+10+10+10*10+10.10+10*10+10+10*10+10.10+10*10+10+10*10+10.10+10+10*10+10+10+10.10+10+10*10+10+10.10*10+10+10+10*10+10.10+10*10+10+10*10+10+10.10*10+10+10*10+10+10.10+10*10+10+10*10+10.10+10*10+10+10*10+10.10+10+10*10+10+10*10^C

Monitoring finalisation!

0 32  =&gt; 316.919 [316.867...317.895] ~0.178511 (0.056327%)

1 59  =&gt; 170.955 [168.583...173.296] ~0.92472 (0.540914%)

2 132 =&gt; 76.9796 [76.942...77.9524]  ~0.085977 (0.111688%)

Первое, что мы должны отметить, — это очень приличную точность выдержки периода синхронизации (последняя колонка вывода). Для того чтобы убедиться в том, что целевая функция при этом выполняется под приоритетом породившего ее потока, закомментируем строки, выделенные жирным шрифтом в коде программы:

# t3

+25+25*5+25+25.15*5+25+25*5+25+25.15*5+25+25+25*5+25.15+25*5+25+25*5+25.15+25*5+25+25*5*5+25.15+25+25*5+25+25*5.15+25+25*5+25+25.15*5+25+25+25*5+25.15+25*5+25+25*5+25+25.15*5+25+25*5+25+25^C

Monitoring finalisation!

0 32 =&gt; 316.919 [316.797...317.915] ~0.185331 (0.0584792%)

вернуться

26

Здесь применена только простейшая форма «активного объекта»: гораздо сложнее, к примеру, определить деструктор такого объекта. Но именно в своем простейшем виде это многообещающая техника активных объектов.