КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Синхронизация потоков семафорами#include <stdlib.h> #include <stdio.h> #include <string,h> #linclude <inttypes.h> #include <iostream.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <semaphore.h> unsigned long n = 1000; static sem_t* sem; static bool debug = false; static char* str; // строка диагностики static volatile int ind = 0; uint64_t *t; void* threadfunc (void* data) { unsigned long i = 0; char tid[ 8 ]; sprintf(tid, W, pthread_self()); // временная метка начала во всех потоках устанавливается //на время //достижения этой точки в последнем (активном) потоке if((int)data == Т - 1) uint64_t с - ClockCyclesO; for(int i = 0; i < T; i++) t[ i ] = c // рабочий цикл переключений за счет синхронизации while(i++ < N) sem_wait(sem + (int)data); if(debug) str[ ind++ ] = *tid; sem_post(sem + ((int)data +1) % T) }; t[ (int)data ] = ClockCyclesO - t[ (int)data ]; return NULL; }; int main(int argc, char *argv[]) { int opt, val; while ((opt = getopt(argc, argv, "n:t:v"))!= -1) { switch(opt) { case n': . if(sscanf(optarg, "%i", &val)!= 1) cout << "parse command line error" << endl, exit(EXIT_FAILURE); if(val > 0) N - val; break; case t': if(sscanf(optarg, &val)!= 1) cout «"parse command line error" «endl, exit(EXIT_FAILURE); if(val > 0) T = val; break; case 'v': debug = true; break; default: exit(EXIT_FAILURE); } }; if(debug) str = new char[ T * n + 1 ]; pthread_t* tid = new pthread_t[ T ]; sem = new sem_t[ T J; t = new uint64_t[ T ]; for(int i = 0: i < T; i++) // все потоки, кроме последнего, будут заблокированы // на своих семафорах сразу же после старта if(sem_init(sem + i, 0, (i == (T - 1))? 1: 0)) perror("semaphore init"), exit(EXIT_FAILURE); if(pthread_create(tid + i, NULL, threadfunc, (void*)i)!= EOK) perror("thread create error"), exit(EXIT_FAILURE); }; for(int i = 0; i < T; i++) pthread_join(tid[ i ], NULL); for(int i = 0; i < T; i++) sem_destroy(sem + i); delete [] sem; for(int i = 0; i < T; i++) cout «tid[ i ] << "\t: cycles - " << t[ i ]<< ";\ton semaphore - " «t[ i ] / T / N «endl; delete [3 tid; delete [] t; if(debug) { str[ ind ] = '\0'; cout «str «endl; delete [] str; }; exit(EXIT_SUCCESS); }; Логически приложение изменилось следующим образом: Теперь у нас может быть не 2 идентичных (симметричных) потока, а произвольное их количество (ключ -t при запуске приложения). Потоки синхронизируются не на одном семафоре - введен массив семафоров по числу потоков: каждый поток блокируется на «своем» семафоре, но разблокирует его (после очередного выполнения своего фрагмента) семафор заблокированного «соседа». Теперь нам нет нужды использовать барьер для одновременного старта всех созданных потоков: семафоры всех создаваемых потоков инициализируются нулевым значением; стартующий поток тут же блокируется на своем семафоре, и только последний из запущенных выполняется, не блокируясь на семафоре. Из кода исключены какие бы то ни было средства принудительной передачи управления (sched_yield()) - все управление логикой ветвления осуществляется только состояниями семафоров. Посмотрим, что у нас получилось. Запускаем приложение с диагностическим выводом идентификаторов потоков (ключ -v; он у нас был в тестах и ранее, только мы о нем не упоминали): # nice -n-19 sy2l -n20 -t12 -v : cycles - 664874; on semaphore - 2770 : cycles - 649150; on semaphore - 2704 ; cycles - 622987; on semaphore - 2595 : cycles - 611781; on semaphore - 2549 ; cycles - 594515; on semaphore - 2477 : cycles - 571003; on semaphore - 2379 : cycles - 536817; on semaphore - 2236 ; cycles - 500388; on semaphore – 2084 ; cycles - 296633; on semaphore - 1235 D23456789ABCD23456789ABCD23456789ABCD23456789ABCD23456789ABCD23456789ABCD234 56789ABCD23456789ABC023456789ABCD23456789ABCD23456789ABCD23456789ABCD2345678 9ABCD23456789ABCD23456789ABCD23456789ABCD23456789ABCD23456789ABC023456789ABC D23456789A8C В строке диагностики (внизу) хорошо видно регулярное чередование выполняющихся потоков, причем оно начинается с индекса последнего созданного потока (13 в показанном примере). Теперь проделаем то же самое, но на большой выборке и с отключенной диагностикой, чтобы получить «чистое» время контекстных переключений потоков, не искаженное затратами на операции формирования диагностики (результаты для нескольких различных размерностей задачи при разном количестве потоков):
# nice 2 tt nice.-n-19 sy21 -пЮОООО -t7
on semaphore - 1281-n-19 sy21 -n50000 -t13: cycles - 832789852;
Есть некоторая корреляция времени переключения контекста с размером выборки и количеством обрабатывающих потоков, но она в широком диапазоне этих параметров не превышает 8%. В данном приложении эта численная величина включает в себя: блокирование на семафоре, переключение на контекст другого потока и разблокирование семафора. Если вспомнить, что раньше мы получали оценки для принудительного (посредством sched_yield()) переключения контекста потоков в 375 процессорных циклов, а для захвата-освобождения семафора - порядка 870, то эти цифры хорошо согласуются с полученными сейчас результатами. Рассматриваемые примитивы служат принципиально различным целям. Мьютекс, как уже было сказано ранее, предназначен в первую очередь для регламентации доступа к участкам программного кода. Семафоры же больше предназначены для регламентации порядка доступа к определенным объектам данных. Классическими задачами этого класса являются задачи «производитель - потребитель», когда М производителей создают некоторые объекты данных (читая эти данные с реальных внешних устройств, или создавая их как результат только внутренних вычислений, или любым другим способом), а N потребителей независимо берут произведенные объекты данных на последующую обработку. Это настолько общий и часто встречающийся класс задач, что покажем для него простейший «скелет» в виде отдельного приложения, в котором отслеживание порядка доступа потребителей будет осуществлять счетный семафор (файл sy22.cc). Для простоты понимания приложение сделано как трансформация кода предшествующей группы тестов. В качестве имитации производства объекта данных, как и в качестве его обработки потребителем, используется пассивная пауза (delayO) на случайную величину (производство и обработка объектов данных в коде не показаны, так как это не относится к существу рассматриваемого - нас интересуют процессы синхронизации этих операций, а не сами операции). Кроме основной нашей цели это приложение дополнительно демонстрирует: Практическое использование принудительного завершения (отмены) потоков «извне» с управлением состоянием завершаемости потоков и расстановкой точек отмены, о чем мы уже говорили ранее. Использование атомарных (непрерываемых) операций (например, atomic_add_value()), о которых мы будем говорить чуть позже. Использование реентерабельных форм функций стандартной библиотеки, безопасных в многопоточной среде (rand_г() вместо rand()).
Дата добавления: 2014-12-10; Просмотров: 385; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |