Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[44.192.95.161] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Суть задачи. Рисуем, например, летающие шарики в одном окне, каждый шарик - отдельный поток (CBall : public CWinThread), в котором вычисляется положение шарика и рисуется сам шарик.
Задача синхронизировать потоки (чтобы шарики не обгоняли друг друга), т.е. каждый поток (в CBall::Run()), прежде чем в очередной раз вычислять и рисовать себя, должен дождаться, чтобы все потоки отрисовались в предыдущей итерации. Шарики будут в list<CBall*>. Шары можно динамически добавлять/удалять прямо во время работы. Необходимо использование Event'ов (на каждый шар - Event окончания отрисовки), но ::WaitForMultipleObjects() или CMultiLock работают только с массивами Event'ов, c vector'ами или list'ами - нет. Делать массив - некрасиво (в программе все на STL), т.к количество шаров(потоков) динамически меняется. Можно что-нить придумать ? |
Сообщ.
#2
,
|
|
|
Что-то не очень понятно что нужно сделать.
Что значит "чтобы шарики не обгоняли друг друга"? И что значит "предыдущая итерация"? Ты как их вообще отрисовываешь? И нафига тебе евенты? Что с чем ты собираешься синхронизировать? |
Сообщ.
#3
,
|
|
|
Не совсем понял зачем тогда потоки... Ели они отрисовываются последовательно. Мобыть примитивный таймер решает?
|
Сообщ.
#4
,
|
|
|
to Олег М, Uncle_Bob
ТЗ таково, что отрисовка и перемещение (вычисление следующей позиции) шара должны быть в одном потоке для каждого шара (каждый шар - это отдельный поток). >>Что значит "чтобы шарики не обгоняли друг друга"? Если не будет синхронизации, то за N-ое количество времени одни потоки могут несколько раз переместить и нарисовать шар (себя), в то время как другие могут вообще ни разу не нарисоваться (не получить управления, разделяемый ресурс - контекст устройства). >>И что значит "предыдущая итерация"? >>Ты как их вообще отрисовываешь? В функции CBall::Run()(унаследованной от класса CWinThread) примерно так: CBall::Run() { CDC dc; dc.Attach(m_hDC); while(!bDone) { ////////////////////////////////// //здесь я хотел что-то типа WaitForMultipleObjects(Кол-во_шаров,FinishDrawEvents[],true,INFINITE); ////////////////////////////////// NewPosition.x=...; //вычисление следующей позиции шара NewPosition.y=...; ... // блокируем доступ к контексту устройства или // ожидаем возможность доступа к контексту // устройства и получив его блокируем доступ // для других потоков EnterCriticalSection(&CritSect); { SelectObject(WhiteBrush); Ellipse(OldPosition); //стираем предыдущую нарисованную позицию шара SelectObject(ColorBrush); Ellipse(NewPosition); //рисуем новую позицию } LeaveCriticalcalSection(&CritSect); OldPosition.x=NewPosition.x; OldPosition.y=NewPosition.y; ////////////////////////////////// //здесь я хотел что-то типа Event[m_ID].SetEvent; ////////////////////////////////// Sleep(20); //даем поработать другим потокам } dc.Detach(); //конец потока } >>И нафига тебе евенты? >>Что с чем ты собираешься синхронизировать? Я хотел синхронизировать потоки друг с другом, чтобы поток, выйдя из Sleep'а гарантированно получил управление только после того, как все потоки (шары) отрисуют себя. Т.е. в CBall::Run() нарисовали себя, доходим до Sleep - даем поработать другим потокам. Когда выходим из Sleep, нужно чтобы следующее прохождение (итерация) while осуществлялась, только если остальные потоки тоже завершили свою отрисовку. А в таком виде, как сейчас, поток, выйдя из Sleep, может опять получить управление, в то время как другие потоки еще не закончили текущие перемещение и отрисовку, т.е. этот шар будет перемещаться быстрее остальных. Вот я и хотел ввести для каждого шара Event FinishStep, и в WaitForMultipleObjects(...) дожидаться пока все отрисуют очередной цикл. Так вот и вопрос, как сделать не массив Event'ов, а что-то типа vector'а, т.к. количество шаров (и Event'ов соответственно) динамически меняется. |
Сообщ.
#5
,
|
|
|
предыдущий Guest это я.
|
Сообщ.
#6
,
|
|
|
Цитата Schwarz, 24.08.04, 09:31 каждый поток (в CBall::Run()), прежде чем в очередной раз вычислять и рисовать себя, должен дождаться, чтобы все потоки отрисовались в предыдущей итерации. Зачем нужны потоки если шары должны рисоваться последовательно один за другим? |
Сообщ.
#7
,
|
|
|
Цитата Guest, 24.08.04, 17:48 сли не будет синхронизации, то за N-ое количество времени одни потоки могут несколько раз переместить и нарисовать шар (себя), в то время как другие могут вообще ни разу не нарисоваться (не получить управления, разделяемый ресурс - контекст устройства). С чего ты взял? Если потоки с одинаковым приоритетом - всё нормально будет, никто ни кого не обгонит. Цитата Guest, 24.08.04, 17:48 В функции CBall::Run()(унаследованной от класса CWinThread) примерно так: А нафига ты так делаешь - отрисовывай их все в одном потоке. |
Сообщ.
#8
,
|
|
|
M Schwarz, Для удобства всех пользователей настоятельно рекомендую пользоваться тегом CODE (Правила , п.4) В общем, использование потоков здесь, конечно, надуманно. Но выход есть. Создать еще один поток, который знает ID всех шарико-потоков Этот вспомогательный поток будет по очереди перебирать ID потоков и посылать им некое сообщение (через PostThreadMessage). Потоковая функция шарико-потока выглядеть будет примерно так: DWORD WINAPI SharikThreadFunc (LPVOID pParam) { ... BOOL bRet; MSG msg; while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if (bRet == -1) { // error hndling & exit thread } switch (msg) { case WMU_DRAWBALL: // пользовательское // вычисляем позицию шарика и рисуем его SetEvent (hJobFinished); break; default: // надо ли это? } } return 0; } А вот функция вспомогательного потока: DWORD WINAPI HelperThreadFunc (LPVOID pParam) { ... for (i=... ) // для всех шарикопотоков { // Достаем ID потока DWORD dwThreadID = ... [i]; // Пошлем i-му потоку сообщение PostThreadMessage (dwThreadID, WMU_DRAWBALL, 0, 0); // Подождем, пока он его обработает WaitForSingleObject (hJobFinished, ...) } ... } Естественно, hJobFinished должен быть создан как autoevent, в исходном невзведенном состоянии. И используй WinAPI потоки, что тебе эти MFC Threads сдались? Там такого лешего понакручено, что в повседневной жизни и не требуется |
Сообщ.
#9
,
|
|
|
Всем сеньк, особенно Uncle_Bob.
Не желают шарики синхронно перемещаться, даже с event'ами. Когда выстраиваешь их в вертикальную линию, и запускаешь на перемещение туда-сюда по горизонтали, то разъезжаются они маленько друг относительно друга, особенно когда начинаешь окно мышкой таскать. |
Сообщ.
#10
,
|
|
|
Так все же, почему не обойтись единственным потоком для всех шариков?
А вообще... Ну хорошо. Давайте ее решим, просто из спортивного интереса Схема такая: каждый поток ждет своего евента и выполняется один раз. Когда все потоки исполнились, евенты ставятся заново. Делаем так: #define NumThreads 32 HANDLE g_GuardEvent; DWORD g_ThreadActions; HANDLE g_ThreadEvents[NumThreads]; //Надо динамический? Сделашь сам, это не влияет на суть задачи void SetAllEvents() { g_ThreadActions = 0; for (int i=0; i < NumThreads; i++) { SetEvent(g_ThreadEvents[i]); } } UINT t_ThreadFn(void* p_Param) { DWORD l_MyNumber = (DWORD) p_Param; for (;;) { WaitForSingleObject(g_ThreadEvents[l_MyNumber]); WaitForSingleObject(g_GuardEvent); if (++g_ThreadActions == NumThreads) { SetAllEvents(); } SetEvent(g_GuardEvent); //А тут уже все что вам надо } } void Startup() { int i; for (i=0; i < NumThreads; i++) { g_ThreadEvents[i] = CreateEvent(NULL, 0, 0, NULL); } g_GuardEvent = CreateEvent(NULL, 0, 1, NULL); //Создание всех потоков for (i=0; i < NumThreads; i++) { AfxBeginThread(t_ThreadFn, (void*)i); } //Разрешаем всему этому начать крутиться :) SetAllEvents(); } Добавлено в : ЗЫ: писал второпях, наверняка где-нить слегка ляпнул... |
Сообщ.
#11
,
|
|
|
Цитата Schwarz, 25.08.04, 12:22 Когда выстраиваешь их в вертикальную линию, и запускаешь на перемещение туда-сюда по горизонтали, то разъезжаются они маленько друг относительно друга, особенно когда начинаешь окно мышкой таскать. В аттаче - иллюстрация моей идеи (35.3 Кб). Разъезжаются может потому, что погрешность в расчетах? Прикреплённый файлSyncThreads.zip (35.39 Кбайт, скачиваний: 234) |
Сообщ.
#12
,
|
|
|
HOMO_PROGRAMMATIS, Uncle_Bob - усе работает, спасибо !
Похоже что я с CMultiLock'ами перемудрил. Буду разбираться. А то я уже на винды бочку катить начал |
Сообщ.
#13
,
|
|
|
Тебе нужно прочитать "как программируют студенты" )
Что-то в духе 1. Все что может быть закодировано немедленно кодируется 2. Возникают баги, 3. Их пытаются отладить 4. Ничего не получается (При этом обязательно упояминается Билл Гейтс) 5. Все стирается и кодируется заново |
Сообщ.
#14
,
|
|
|
Блин, может я уже зациклился, но ведь должно же так работать.
Один поток - управляющий, который ждет, когда все потоки-шарики сделают по "шагу"(переместятся и нарисуются, выставят event evFinishStep[ID] ), тогда для каждого потока-шарика управляющий поток выставит разрешение сделать очередной "шаг" - event evStart[i]). Однако как-то "не досинхронизируется" это дело. И еще - в дебажной версии постоянно вылетает на какой-то UserBreakpoint, что-то c CWinThread. В релизной - работает. #define NumObj 5 extern CEvent* evFinishStep[NumObj]; extern CEvent* evStart[NumObj]; // Рабочая функция потока-шарика int CObj::Run() { CDC dc; dc.Attach(m_hDC); CBrush WB(RGB(255,255,255)); //white brush CBrush CB(RGB(255,0,0)); //color brush CPen WP(PS_SOLID,1,RGB(255,255,255)); //white pen CPen CP(PS_SOLID,1,RGB(255,0,0)); //color pen CBrush *pOldB; CPen* pOldP; RECT rw; pOldB=dc.SelectObject(&WB); pOldP=dc.SelectObject(&WP); CSingleLock sl(evStart[this->m_nID]); while(!bDone){ sl.Lock(); //ждать разрешения от ObjMgr m_pWnd->GetClientRect(&rw); CurPosX+=OffsetX;CurPosY+=OffsetY; if((CurPosX+R>=rw.right)||(CurPosX-R<=rw.left)){OffsetX*=-1;} if((CurPosY+R>=rw.bottom)||(CurPosY-R<=rw.top)){OffsetY*=-1;} cs.Lock(); { dc.SelectObject(&WB); dc.SelectObject(&WP); dc.Ellipse(OldPosX-R,OldPosY-R,OldPosX+R,OldPosY+R);//стираем старый dc.SelectObject(&CB); dc.SelectObject(&CP); dc.Ellipse(CurPosX-R,CurPosY-R,CurPosX+R,CurPosY+R);//рисуем новый GdiFlush(); } cs.Unlock(); OldPosX=CurPosX;OldPosY=CurPosY; evFinishStep[m_nID]->SetEvent();//сказать, что закончен очередной шаг Sleep(1); } if(pOldB)dc.SelectObject(pOldB); if(pOldP)dc.SelectObject(pOldP); dc.Detach(); return CWinThread::Run(); } // Рабочая функция потока-manager'а int CObjMgr::Run() { CMultiLock ml((CSyncObject**)evFinishStep,NumObj,0); while(1){ ml.Lock(); //ждать, когда все потоки завершат очередную // отрисовку и перемещение for(int i=0;i<NumObj;i++){ evStart[i]->SetEvent(); //разрешить каждому потоку //переместиться и нарисоваться } } return CWinThread::Run(); } // Запуск всего этого безобразия void CSW_tmpView::OnTestGo() { CObj* pObj; CObj::bDone=false; int x=50,y=50; CEvent* pEv; // создать события разрешения работы для // потоков-шариков for(int i=0;i<NumObj;i++){ pEv=new CEvent; //с автосбросом pEv->SetEvent(); //изначально разрешить всем //потокам-шарикам начать работать evStart[i]=pEv; } // создать события окончания очередного шага перемещения и отрисовки // потоков-шариков (для потока-managerа) for(int i=0;i<NumObj;i++){ pEv=new CEvent; //с автосбросом pEv->ResetEvent(); //изначально поток-manager ждет evFinishStep[i]=pEv; } // создать поток-manager CObjMgr* pObjMgr=new CObjMgr; pObjMgr->CreateThread(); // создать потоки-шарики for(int i=0;i<NumObj;i++){ pObj=new CObj; pObj->Init((CWnd*)this,GetDC()->GetSafeHdc(),x,y+=25); pObj->m_nID=i; pObj->CreateThread(); } } Или это MFC-ные потоки и синхр.объекты такие глючные ? |
Сообщ.
#15
,
|
|
|
Schwarz, глянь мой пример и сравни со своим... Тебе всего навсего, надо переписать один метод из моего примера, и все заработает Какие-то мультилоки, винтреды, черт в стуле
|