На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела Visual C++ / MFC / WTL (далее Раздела)
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.

Полезные ссылки:
user posted image FAQ Раздела user posted image Обновления для FAQ Раздела user posted image Поиск по Разделу user posted image MSDN Library Online
Модераторы: ElcnU
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Синхронизация потоков (MFC) , CMultiLock with vector<CEvent> ?
    Суть задачи. Рисуем, например, летающие шарики в одном окне, каждый шарик - отдельный поток (CBall : public CWinThread), в котором вычисляется положение шарика и рисуется сам шарик.
    Задача синхронизировать потоки (чтобы шарики не обгоняли друг друга), т.е. каждый поток (в CBall::Run()), прежде чем в очередной раз вычислять и рисовать себя, должен дождаться, чтобы все потоки отрисовались в предыдущей итерации. Шарики будут в list<CBall*>. Шары можно динамически добавлять/удалять прямо во время работы.
    Необходимо использование Event'ов (на каждый шар - Event окончания отрисовки), но ::WaitForMultipleObjects() или CMultiLock работают только с массивами Event'ов, c vector'ами или list'ами - нет. Делать массив - некрасиво (в программе все на STL), т.к количество шаров(потоков) динамически меняется.
    Можно что-нить придумать ?
      Что-то не очень понятно что нужно сделать.
      Что значит "чтобы шарики не обгоняли друг друга"?
      И что значит "предыдущая итерация"?
      Ты как их вообще отрисовываешь?
      И нафига тебе евенты?
      Что с чем ты собираешься синхронизировать?
        Не совсем понял зачем тогда потоки... Ели они отрисовываются последовательно. Мобыть примитивный таймер решает?
          to Олег М, Uncle_Bob

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

          >>Что значит "чтобы шарики не обгоняли друг друга"?
          Если не будет синхронизации, то за N-ое количество времени одни потоки могут несколько раз переместить и нарисовать шар (себя), в то время как другие могут вообще ни разу не нарисоваться (не получить управления, разделяемый ресурс - контекст устройства).
          >>И что значит "предыдущая итерация"?
          >>Ты как их вообще отрисовываешь?

          В функции CBall::Run()(унаследованной от класса CWinThread) примерно так:
          ExpandedWrap disabled
            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'ов соответственно) динамически меняется.
          Сообщение отредактировано: Uncle_Bob -
            предыдущий Guest это я.
              Цитата
              Schwarz, 24.08.04, 09:31
              каждый поток (в CBall::Run()), прежде чем в очередной раз вычислять и рисовать себя, должен дождаться, чтобы все потоки отрисовались в предыдущей итерации.


              Зачем нужны потоки если шары должны рисоваться последовательно один за другим?
                Цитата
                Guest, 24.08.04, 17:48
                сли не будет синхронизации, то за N-ое количество времени одни потоки могут несколько раз переместить и нарисовать шар (себя), в то время как другие могут вообще ни разу не нарисоваться (не получить управления, разделяемый ресурс - контекст устройства).

                С чего ты взял? Если потоки с одинаковым приоритетом - всё нормально будет, никто ни кого не обгонит.

                Цитата
                Guest, 24.08.04, 17:48
                В функции CBall::Run()(унаследованной от класса CWinThread) примерно так:

                А нафига ты так делаешь - отрисовывай их все в одном потоке.
                  M
                  Schwarz,

                  Для удобства всех пользователей настоятельно рекомендую пользоваться тегом CODE (Правила , п.4)


                  В общем, использование потоков здесь, конечно, надуманно. Но выход есть. Создать еще один поток, который знает ID всех шарико-потоков :) Этот вспомогательный поток будет по очереди перебирать ID потоков и посылать им некое сообщение (через PostThreadMessage).

                  Потоковая функция шарико-потока выглядеть будет примерно так:
                  ExpandedWrap disabled
                    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;
                    }


                  А вот функция вспомогательного потока:

                  ExpandedWrap disabled
                    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 сдались? Там такого лешего понакручено, что в повседневной жизни и не требуется :)
                    Всем сеньк, особенно Uncle_Bob.

                    Не желают шарики синхронно перемещаться, даже с event'ами. Когда выстраиваешь их в вертикальную линию, и запускаешь на перемещение туда-сюда по горизонтали, то разъезжаются они маленько друг относительно друга, особенно когда начинаешь окно мышкой таскать.
                      Так все же, почему не обойтись единственным потоком для всех шариков?

                      А вообще... Ну хорошо. Давайте ее решим, просто из спортивного интереса :)
                      Схема такая: каждый поток ждет своего евента и выполняется один раз. Когда все потоки исполнились, евенты ставятся заново.
                      Делаем так:


                      ExpandedWrap disabled
                         #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();
                        }


                      Добавлено в :
                      ЗЫ: писал второпях, наверняка где-нить слегка ляпнул...
                        Цитата
                        Schwarz, 25.08.04, 12:22
                        Когда выстраиваешь их в вертикальную линию, и запускаешь на перемещение туда-сюда по горизонтали, то разъезжаются они маленько друг относительно друга, особенно когда начинаешь окно мышкой таскать.

                        В аттаче - иллюстрация моей идеи (35.3 Кб).

                        Разъезжаются может потому, что погрешность в расчетах?
                        Прикреплённый файлПрикреплённый файлSyncThreads.zip (35.39 Кбайт, скачиваний: 224)
                          HOMO_PROGRAMMATIS, Uncle_Bob - усе работает, спасибо !

                          Похоже что я с CMultiLock'ами перемудрил. Буду разбираться. А то я уже на винды бочку катить начал :)
                            Тебе нужно прочитать "как программируют студенты" :))
                            Что-то в духе
                            1. Все что может быть закодировано немедленно кодируется
                            2. Возникают баги,
                            3. Их пытаются отладить
                            4. Ничего не получается (При этом обязательно упояминается Билл Гейтс)
                            5. Все стирается и кодируется заново
                              Блин, может я уже зациклился, но ведь должно же так работать.
                              Один поток - управляющий, который ждет, когда все потоки-шарики сделают по "шагу"(переместятся и нарисуются, выставят event evFinishStep[ID] ), тогда для каждого потока-шарика управляющий поток выставит разрешение сделать очередной "шаг" - event evStart[i]).
                              Однако как-то "не досинхронизируется" это дело.
                              И еще - в дебажной версии постоянно вылетает на какой-то UserBreakpoint, что-то c CWinThread. В релизной - работает.

                              ExpandedWrap disabled
                                 
                                #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-ные потоки и синхр.объекты такие глючные ?
                                Schwarz, глянь мой пример и сравни со своим... Тебе всего навсего, надо переписать один метод из моего примера, и все заработает :) Какие-то мультилоки, винтреды, черт в стуле :wacko:
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0525 ]   [ 16 queries used ]   [ Generated: 25.04.24, 10:26 GMT ]