Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Синхронизация потоков


Автор: Agentx86 8.12.2007, 13:13
В программе есть несколько потоков которые делают какието вычисления. Надо когда все потоки закончат вычисления просумировать все данные и чтото выполнить. Вот я для этого использовал функцию WaifForMultiplyObject. Эта функция ждет завершение всех поток и только после этого программа начинает работать дальше. Как можно сделать тоже самое используя синхронизацию потоков, но не используя данную функцию?

Автор: Cycle 8.12.2007, 23:33
Каждому процессу раздаешь по мутексу, по окнчании работы каждый процесс освобождает мутекс. Процесс, который должен просуммировать все данные, должен попытатся захватить все мутексы. Ему это удастся, когда все остальные процессы освободят свои мутексы.

Также вроде бы подобное можно промутить с критическими секциями. Потому как EnterCriticalSection - это аналог захвата мутекса, а LeaveCriticalSection - это аналог его освобождения. Надеюсь, что с критическими секциями я ничего не напутал smile

В MSDN написано, что
Цитата

Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization.

Поэтому, если эта операция у тебя происходит в цикле, то использование критических секций будет работать в 2-3 раза быстрее, чем мутексы. Проверено на практике smile

Автор: Earnest 11.12.2007, 21:09
Цитата(Cycle @  9.12.2007,  00:33 Найти цитируемый пост)
Потому как EnterCriticalSection - это аналог захвата мутекса, а LeaveCriticalSection - это аналог его освобождения. Надеюсь, что с критическими секциями я ничего не напутал 

Ведь сам же ниже привел цитату "... except ". 
Не годятся критические секции для ожидания - только для простой синхронизации доступа к данным. Простой в том смысле, что доступ будет разрешен только одному потоку, и не важно, что он там делает.

Agentx86, синхронизация, но без Wait-функций - это примерно то же самое, что манная каша, но без манки - не бывает.
То, что ты сделал - наилучший способ - ждать завершения всех потоков, засунув список хандлов в WaitForMultipleObject. Нафиг тут мьютексы и прочая не нужны. Тем более, что и их через Wait ждать придется - больше нечем.

Т.е. нет, пардон, можно все тоже самое написать не через объекты ядра, а на коленках, скажем, заведя глобальную переменную (счетчик работающих потоков) и пусть каждый поток перед завершением ее декрементирует... А в основном замутить бесконечный цикл и проверять значение этой переменной... Пальчики оближешь, до чего изящное решение... Это хочешь?

Автор: Agentx86 11.12.2007, 23:28
Все задача решена. А это не моя прихоть так извращаться. Преподы запаривают вопросами - "Я хочу, чтобы вы сделали тоже самое, но не используя эту функцию"

Автор: Cycle 12.12.2007, 10:52
Earnest, нельзя недооценивать возможности критических секций. Ещё раз с акцентирую фразу: 
Цитата

Critical section objects provide synchronization similar to that provided by mutex objects, except that critical section objects can be used only by the threads of a single process.

Т.е. если потоки выполня.тся в пределах одного процесса, то предпочтение нужно отдавать именно критическим секциям, т.к. 
Цитата

critical section objects provide a slightly faster

И как показала практика в 2-3 раза.
А как именно задачу Agentx86 реализовать через мутексы/критические секции я написал выше.
P.S. Я как раз тот студент который удовлетворял прихоти преподов и с ними согласен, так как программист должен думать гибко smile

Автор: Earnest 12.12.2007, 20:26
Cycle, 
Цитата(Cycle @  12.12.2007,  11:52 Найти цитируемый пост)
Earnest, нельзя недооценивать возможности критических секций. 

А кто их недооценивает?
Да, ты прав, можно "раздать" каждому потоку по критической секции, поставив в начале-конце потоковой функции Enter\Leave.
Но, чтобы основной поток дождался освобождения всех секций, ему нужно выполнить Enter в эти критические секции - отдельно в каждую - видимо, в цикле... На мой взгляд, данное решение по своей кривизне аналогично предложенному мной "решению" с глобальным счетчиком. Разве что удовлетворение требования препода может оправдать такое. Но заметь, об этом автор сказал только потом... А почему он не хочет использовать Wait - да мало ли, может ему казалось, что есть решение лучше.
Что касается использования мьютексов, то их, во-первых, их все-таки придется ждать с помощью Wait, а во-вторых - мьютексы в данном контексте избыточны, ибо есть хандлы самих потоков.

Насчет гибкости... не знаю, где тут гибкость, если человек сразу нашел нормальное решение, миновав глупости, которые, возможно, от него ждал преподаватель.
Да, я понимаю, если препод получит от студента решение задачи в лоб, но не самое оптимальное - можно сказать - а подумай-ка, нельзя ли сделать лучше, и подсказать - не используй это, а используй то... А так, он требует, вместо оптимального решения - решение через ж. коленом и, скорее всего, это вовсе не единичный случай. Т.е. как-то жить с этим приходится, но уж говорить, что это правильно - увольте...

Что касается твоего замечания насчет предпочтения мьютексу критической секции в рамках одного процесса - тоже не соглашусь. Т.е. далеко не всегда. Есть вещи, которые можно сделать с помощью мьютекса и нельзя - с помощью секции. Да хотя бы защита от зацикливания или зависания потока - функции Wait можно дать timeout, по окончании которого поток можно просто прибить, а после вызова Enter назад дороги нет...

Добавлено через 7 минут и 43 секунды
Да, еще: запустив все потоки, основной поток сразу начинает ждать освобождения что мьютексов, что критических секций. И где гарантия, что его вызов не окажется первым (т.е. именно он захванит объект, не дав этого сделать потоку)? Такое гораздо чаще встречается на практике, чем хотелось бы думать. Т.е. защититься от этого, конечно, можно, воодя дополнительные механизмы, но это опять лишние усложнения. А без них - это вовсе не решение, т.к. работает не стопроцентно.

Автор: Agentx86 12.12.2007, 22:33
Преподу не понравилось, что я выполнил лабу неиспользовав синхронизацию потоков.

Автор: Simargl 12.12.2007, 23:57
Earnest, пардон, не вижу надобности в цикле.
во вторых описанная ситуация когда поток не выходит из крит секции а другой пытается туда влезть, называется deadlock и вызывается исключительно кривостью рук программиста. А в рамках процесса правильнее использовать крит секции, соглашусь с Cycle, с вопросом почему - к Рихтеру.

Автор: Agentx86 13.12.2007, 01:28
Simargl, Ты тут чтото сказал про криворукость рук. Значит у меня кривые руки. Предложи свой вариант. Причем нельзя вызывать WaitForMultiply или пять раз WaitForSingleObject.
Надо синхронизировать первые 5 потоков а WaitForSingleObject может быть вызвана один раз.

Автор: baldina 13.12.2007, 03:32
Без WaitFor... ничего не выйдет. Критические секции "вроде бы" позволяют, но где гарантия, что вызывающая программа не попытается войти в секцию раньше вызываемого потока? Sleep() в вызывающей увеличивает шансы, но не дает гарантии, а следовательно дает иллюзию. И "нормально работающая" программа на 101м вызове заработает не так.

Agentx86, мне кажется, тут спутаны два понятия: синхронизация выполнения потоков и синхронизация доступа к объекту из разных потоков. В итоге, естесстно, все сводится к одним и тем же механизмам, разница только в ходе размышлений. 
Но: для доступа к объекту весьма подходят критические секции. С их помощью нельзя строго установить очередность, но обеспечивается монопольность доступа. Для синхронизации выполнения кто-то кого-то должен подождать, т.е. обязателен Wait.

Добавлено через 1 минуту и 51 секунду
Интересно, как выглядит правильное решение без wait с точки зрения препода.

Автор: Lazin 13.12.2007, 08:59
критические секции позволяют синхронизировать доступ к объекту, причем они обеспечивают только эксклюзивный доступ к объекту, при этом нет возможности обработать ситуацию, когда поток в котором захвачена секция, забыл ее разлочить))
мютекс, более гибок, можно например просто проверить его состояние, а не ждать, установить timeout на ожидание. Мютекс может быть именованным, а значит его можно использовать для синхронизации процессов. Еще есть разновидность Wait функций, которые просыпаются по сообщению, тут вообще критические секции не применимы.
Код

DOUBLE res = WaitForSingleObjectMsg(mutex, INFINITE);
if (res == WAIT_OBJECT_0)
{
 захвачен мютекс
} else
{
 пришло сообщение
}

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

Автор: Nastya 13.12.2007, 10:23
Вааще не поняла чем не нравится WaitFor... и зачем что-то еще изобретать.
Единственное что если при расчетах идет обращения к одним и тем же данным это аккуратно надо синхронизировать. 

Автор: Agentx86 13.12.2007, 12:20
Если честно я тоже не мог понять препопада и хотябы один раз WaitForSingleObject вызвать надо это без вариантов. А идея какая. Название лабы "Синхронизация потоков", а я её сделал не используя объекты синхронизации. Вот он и начал "А вот я хочу убрать отсюда функцию WaitForMultiplyObject". Я ему говорю "А я поставлю 5 функций WaitForSingleObject". Препод ответчает "Мне так тоже неподойдет. Например первый поток будет работать дольше всего. Мы будет сначала ждать его, а пото только другие которые уже завершились. Потеря времени". Хотя вариант который предложили с критичискими секциями не будет быстрее чем 5 WaitForSingleObject. Он будет работать также. Потомучто мы в шесток потоке пытаемся сделать EnterCriticalSection сначала в первый поток, потом во второй............. .
Как решить эту задачу, чтобы она выполнялась по скорости как c WaitForMultiplyObject и не используя эту функцию я не знаю. Мне кажется препод сам не знает. Я у него на защите лабы поинтерисуюсь.

p.s. Nastya не употребляй слова паразиты. Кстати мы знакомы.

Автор: MAKCim 13.12.2007, 12:48
Agentx86, 
N потоков можно заменить на N - 1 поток
(поток, порождающий потоки, может заменить один из них)
это увеличит производительность

Добавлено через 5 минут и 16 секунд
Цитата(Agentx86 @  8.12.2007,  13:13 Найти цитируемый пост)
все потоки закончат вычисления просумировать все данные и чтото выполнить. 

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

Автор: baldina 14.12.2007, 00:50
Понятно. Препод хотел мьютексов, но не продумал ни задание, ни линию поведения  smile Бывает.
Имхо число WaitFor на скорость не влияет. В том смысле, что общее время работы в любом случае - время работы самого продолжительного потока. Вот если действительно начинать суммировать результаты уже завершенных потоков, как предложил  MAKCim - другое дело. 

Автор: Stranger2048 21.12.2007, 17:41
Ребят, а почему события не попробовать?.. Установить его, когда все потоки завершат свою работу, а поток, котору нужно делать результирующие вычисления, будет как раз этого события и ждать, естественно через один вызов WaitForSingleObject. 
Ну это как вариант для удовлетворения запросов препода. smile Или у него собитие - это не объект синхронизации? smile))

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)