![]() |
|
Модераторы: feodorv, GremlinProg, xvr, Fixin |
![]()
|
|
| wallstreet |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 153 Регистрация: 11.8.2011 Репутация: нет Всего: нет |
Всем доброго дня.
Задача стоит раз в секунду одним потоком выделить область для данных, вторым заполнить, третим отобразить. Период в секунду нужно отобразить скорее всего ожидаемым таймером. Делаю вот так:
В результате таблица один раз отрисовывается и все, хотя третий параметр функции SetWaitableTimer(...,...,1000,...,...,...) равен насколько я понимаю одной секунде. т.е. после первой отрисовки J = 99 и следующая должна начаться с этого значения. Что делаю не так? блин код как-то не так отображаться стал, поэтому нужную строку подсветил Это сообщение отредактировал(а) GremlinProg - 15.3.2012, 07:13 |
|||
|
||||
| Dem_max |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1780 Регистрация: 12.4.2007 Репутация: 16 Всего: 39 |
-------------------- Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte") |
|||
|
||||
| wallstreet |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 153 Регистрация: 11.8.2011 Репутация: нет Всего: нет |
а вот так можно устанавливать время постоянного срабатывания с интервалом 1 сек:
или обязательно создавать переменную системного времени и приводить их к FILETIME а потом уже использовать???? Думаю что нет.. Разве что если нам надо что бы таймер сработал в определенное время, тогда да. А в данном случае мой таймер освобождается для потоков через 10 сек после создания и так через 1 сек в дальнейшем должно быть, но не получается Это сообщение отредактировал(а) wallstreet - 11.3.2012, 21:08 |
|||
|
||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
В коде этого нет. Потоки Thread1, Thread2 и Thread3 отработают один раз и завершатся. Потом. По смыслу задачи, сначала отрабатывает первый поток с выделением памяти, уже потом должен отрабатывать второй поток с инициализацией, а, гм, третий (но есть ещё один наш, изначальный поток) должен заняться отрисовкой (в реальности он лишь посылает сообщение в оконную процедуру). Схема взаимодействия потоков в приведённом коде ужасная((( Её необходимо переделать. Поскольку для отрисовки (в любой момент времени!) данных эти самые данные необходимо иметь, то, действительно, эти данные необходимо защитить от одновременного доступа. Критическая секция здесь подойдёт лучше всего, но не просто потому, что проще всего, а потому, что не имеет принадлежности к потоку, вызвавшему EnterCriticalSection (то есть освободить секцию мы можем в любом другом потоке!). Именно этим свойством я и предлагаю воспользоваться. Но ещё раз скажу, что задача осложняется тем, что:
Таким образом получается: Первый поток Цикл по:
Цикл по:
Цикл по:
Однако даже в таком варианте схема имеет изъян. Он связан с тем, что за время между срабатываниями таймера второй поток может не успеть проинициализировать данные, и при следующем срабатывании таймера первый поток начнёт перезаказывать память, а второй будет всё ещё инициализировать выделенную в прошлый раз память. Такая ситуация лечится тем, что придётся ввести вторую критическую секцию, которой обычным образом дополнительно защищаются данные в первом и втором потоках, ну с этим Вы справитесь самостоятельно... ЗЫ События 1, 2 и 3 также используются для корректного завершения приложения. Это сообщение отредактировал(а) feodorv - 11.3.2012, 23:17 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| wallstreet |
|
||||||||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 153 Регистрация: 11.8.2011 Репутация: нет Всего: нет |
Огромное спасибо всем за участие.
Надо сделать. В этом собственно и загвоздка. Из определения: ОЖИДАЕМЫЕ ТАЙМЕРЫ - объекты ядра, которые самосоятельно переходят в свободное состояние в определенное время или через регулярные промежутки времени. Вот мне этот промежуток и не удается задать(( Ну а теперь по порядку.
А кто событие для первого потока будет освобождать? Предполагаю что поток 3й или раздел отрисовки окна WM_PAINT?
Насколько я понял, чо проверять должны на выполнение условия, т.е. применительно к потоку1, память под массив выделена, значит освобождаем событие1 переходим во второй поток который завершает первый или как? Вот что получилось:
Вообщем, так, к слову, в задаче нужно было обойтись исключительно критическими секциями. Но для меня важно уметь использовать все средства взаимодействия, поэтому благодарю за саппорт в обучении и надеюсь поможете разобраться с таймером его периодичностью и другими вопросами. Это сообщение отредактировал(а) wallstreet - 12.3.2012, 18:32 |
||||||||
|
|||||||||
| feodorv |
|
||||||||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
Было бы проще, если бы в доступности находился весь проект)))
Несколько замечаний: 1/
Если мы создаём события со сбросом вручную, то
Правда, тогда нужны соответствующие ResetEvent в нужных местах... 2/ Всё-таки мы сначала ждём таймера, а уже потом захватываем cs1... Этим мы даём возможность окну отрисовать клиенскую область при разных событиях (схлопывание-раскрытие окна, перекрытие окна, завершение работы...):
Ну и устанавливать события можно вне сферы деятельности критической секции... 3/ Имелось ввиду, что если потоки будут бесперерывно работать, то их нужно корректно завершить при завершении всего приложения (при IDM_EXIT). Обычно это делается так: заводится глобальная булева переменная (назовём её done), которая принимает значение TRUE только при необходимости завершить работу, а все созданные потоки пробуждаются тем или иным способом. Но поскольку Thread1 висит исключительно на таймере, то для завершения потока нужно дожидаться срабатываения таймера (для 1 секунды это терпимо, а если нужно было раз в минуту?) Для этого и вводилось соответствующее событие в Thread1, которое сбрасывалось бы только при IDM_EXIT. 4/ Вот это отсутствует
Иными словами, нужно так:
Аналогично и в других потоках!!! 4/ Завершение приложения - отдельная песня. Ибо необходимо дождаться завершения всех трёх потоков через WaitForMultipleObjects, предварительно установив done в TRUE и пробудив потоки через SetEvent. Ждать можно максимум 10 секунд...
И уже потом DestroyWindow()... 5/ при отрисовке нужно учитывать, что buf может быть NULL (например, если отрисовка произойдёт быстрее, чем первый поток успеет захватить критическую секцию), тогда просто можно ничего не рисовать. Гм, нужно также проследить, что Вы очищаете клиенскую область окна при перерисовке... Мы будем ждать или-или (а не и-и), поэтому если сработает таймер, то и поток1 пробудится. Про событие и его роль уже написал Во-первых, потоки отрабытывают всего один раз, сколько бы раз там таймер не срабатывал... Во-вторых, зачем эта задержка:
Почему не просто 0? А так, на вид, всё правильно))) В сущности, можно было бы начать писать программу с одного потока, сидящего на таймере, а два других добавить позже, когда таймер заработает...
Ну, эээ, теоретически, можно всё переписать исключительно через критические секции, но это будет сильно напряжно))) Так что потихоньку двигаемся к финишу))) Это сообщение отредактировал(а) feodorv - 12.3.2012, 23:48 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||||||||||
|
|||||||||||||||||
| wallstreet |
|
||||||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 153 Регистрация: 11.8.2011 Репутация: нет Всего: нет |
Ну вроде подогнал под ваши рекомендации.
По пунктам: 1\ А зачем нам создавать события с ручным сбросом. Мы дождались освобождения события и оно автоматом занимается потоком дождавшимся. Т.о. мы создаем событие занятым изначально, потом первый поток его освобождает для второго, а второй дождавшись занимает ну и тд. 2\ реализовал по примеру 4\ 3\ реализовал в IDM_EXIT после чего при попытке выхода путем Menu\Exit прога висит секунд 5, потом закрывает окно. Однако для меня немного непонятным остается, что значит:
4\ В целом не понимаю зачем завершать поток при выходе? Он ведь в рамках процесса окна создается, насколько я понимаю, соответственно и завершится по завершению процесса окна при выходе. Ну и путаница в голове по поводу освобождения событий возникает. Допустим осбытие0 освободилось, но при проверке done на TRUE нас сразу выкинет из потока но не завершит. Ведь насколько я понимаю завершение потока происходит с помощью функции _endthreadex(). Т.о. почему бы просто не взять и не завершить этой функцией все потоки по очереди в IDM_EXIT? Вобщем хотелось бы поподробнее о выходе. 5\ Вроде все ок.
для того что бы визуально видеть что таймер срабатывает. А так можно и 0 Кстати что делать с отрисовкой раз в секунду моих данных? Архив с проектом
Большое спасибо за помощь. Это сообщение отредактировал(а) wallstreet - 13.3.2012, 14:00 |
||||||
|
|||||||
| feodorv |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
Спасибо! В конце концов удалось скомпилиться и запуститься)))) Просто у Вас ошибка в Thread3: Мало того, что там совсем не нужно защищать код критической секцией, так ещё и EnterCriticalSection(&cs2); два разА прописано (типичный копи-паст баг)))) СтОит только оставить:
И обновление идёт)))) Правда, как я уже говорил:
Ну, это Вы уж как-нибудь сами))) Ну, в данном случае не нужно))) Так как у нас один поток = одно событие. При другом раскладе стОит сделать выбор...
А у меня не висит ни секундочки (release) В первоначальном варианте события hEvent0 не было, а без него поток пробуждался исключительно по таймеру. И сделав WaitForObject(s) (hThread1) мы бы зависли до тех пор, пока не сработал бы таймер, пробудив поток. Используя hEvent0, мы не зависим от таймера (при пробуждении первого потока).
Где?))) _endthreadex() вызывается функцией _beginthreadex() по окончании выполнения функции потока. А Вы функцию потока завершать никак не хотели))) И мы бы остались с тремя потоками при завершении программы. Их бы всё равно прибила операционная система (при возврате из WinMain) посредством TerminateThread, но этого допускать не следует, так как при внезапной кончине одного потока другой поток своими действиями может вызвать нарушение доступа.
Гм. Вот как раз по тем самым причинам. Представьте себе, Вы говорите программе "Exit", а она: "Access violation..." Не комильфо))) Самый лучший вариант - дать потоку завершиться самому. -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||||
|
|||||||||||
| wallstreet |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 153 Регистрация: 11.8.2011 Репутация: нет Всего: нет |
Оооо, да.
Ошибка зачетная была... Ну теперь все работает как часы. Примного благодарствую) |
|||
|
||||
| GremlinProg |
|
||||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2706 Регистрация: 9.8.2005 Где: Тюмень Репутация: 99 Всего: 106 |
вот EnterCriticalSection как раз имеет такую принадлежность, а то, что LeaveCriticalSection может освободить чужую секцию можно расценивать как избыточную функциональность, которая может приводить к различным ошибкам
я бы не советовал пользоваться таким свойством
судя по условию, задача учит просто синхронизировать доступ к массиву, управляемому разными потоками, это не значит, что на секциях должно быть все не надо пытаться думать о том, что какой-то поток дожен отработать раньше другого, это движение мыслей неправильное, и приведет лишь к потерям времени, денег, нервов если есть топологические отношения между потоками, значит они должны разрешаться синхронизаторами, по одному на каждое такое отношение, иначе стоит подумать о том, чтобы преобразовать такие потоки в один автомат отобразить, нарисовать - граница нечеткая, но если нарисовать, то здесь основной поток вообще должен просто отобразить готовую картинку, которую третий поток как раз и формирует -------------------- "Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины." |
||||
|
|||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
Ок. Скажу иначе: потоку, захватившему критическую секцию, не передаются права владения ею (в отличии от мютекса). Может, но не обязательно приведёт. С другой стороны, если "an error occurs", то схему лучше переделать. Хотя, честно говоря, не понимаю, какая именно возникает ошибка. То, что неправильное применение критических секций может вызвать взаимную блокировку, несомненно. Интересное замечание))) Но в данном случае это требуется по условию задачи. Если будет такая возможность и желание, предложите свою схему, переделаем На здоровье))) -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
Кажется, всё же, понимаю))) При отсутствии цикла в Thread1 всё было бы нормально. При наличии цикла возможно его зависание... Мдя))) Это сообщение отредактировал(а) feodorv - 15.3.2012, 02:18 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
| GremlinProg |
|
||||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2706 Регистрация: 9.8.2005 Где: Тюмень Репутация: 99 Всего: 106 |
угу
угу + включаем законы мерфи по условию задачи, очередность отработки потоков не регламентируется ) 1. заводим секцию x, охраняющую модификацию/чтение массива 2. заводим событие (или семафор) y, разрешающий перезапись данных массива 3. заводим событие (или семафор) z, разрешающий отрисовку данных массива 4. заводим поток a, который ресайзит массив (с учетом входа в x) и сигнализирует y 5. заводим поток b, который при активации y заполняет массив (с учетом входа в x) и сигнализирует z 6. заводим поток c, который при активации z рисует массив во временный растр (с учетом входа в x) и посылает команду на обновление окна 7. в WM_PAINT просто отрисовать готовый растр, если он имеется 6 и 7 пункт, по хорошему, требует также синхронизации доступа к глобальному растру, решать это можно как секцией, так и простой взаимоблокировкой, p.s.: если не понятно как реализовать синхронизацию доступа к глобальному растру взаимоблокировкой, могу потом показать Добавлено через 8 минут и 10 секунд + для большей интерактивности, на форме можно, разместить манипуляторы, управляющие первым потоком, например: для управления длиной массива и кнопкой, для "спуска" итерации первого потока, тогда потребуется еще одно событие, которого этот поток будет ожидать, перед ресайзом массива и, соответственно, которое будет сигнализировать кнопка -------------------- "Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины." |
||||
|
|||||
| feodorv |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 9 Всего: 45 |
Они всегда включены))) Но если жить только по ним, то лучше не жить)))
Да, согласен. Просто есть желательная последовательность выполнения потоков (согласитесь, странно планировать программу так, чтобы пробуждались потоки в обратной последовательности, то есть сначала пробуждать поток с отрисовкой, потом пробуждать поток с инициализацией, и только потом с выделением памяти). Понятно, что реализовывать программу нужно исходя из того, что порядок выполнения и планирование потоков операцинной состемой может быть произвольными (с нашей, пользовательской, точки зрения). Это решает ОС.
Ок. А метафайл никак подойдёт под эту задачу?
Не понятно, что здесь непонятного, поэтому лучше показать, чтобы рассеять все сомнения)))) -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||
|
|||||||
| GremlinProg |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2706 Регистрация: 9.8.2005 Где: Тюмень Репутация: 99 Всего: 106 |
судя по твоему обширному плану, они выключены, эти законы не для того, чтобы жить по ним, а для того, чтобы не заморачиваться, и жить по своим а процесс разработок - такая штука: чем дальше в лес, тем больше дров - не всегда может опираться на "желательную" логику работы программы, особенно тогда, когда эта логика нестабильна и может быть легко нарушена -------------------- "Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины." |
|||
|
||||
![]()
|
| Правила форума "C/C++: Системное программирование и WinAPI" | |
|
|
На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы . Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |