На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Модераторы: Qraizer
Страницы: (14) « Первая ... 5 6 [7] 8 9 ...  13 14 все  ( Перейти к последнему сообщению )  
> SObjectizer 5.3.0
    извините за нубский вопрос, но я всё равно не понимаю, зачем нужны эти акторы без зеленых потоков? при том, что у меня есть небольшой опыт в Эрланге, т.е. я вроде бы должен понимать, что это за акторы.
    мне кажется, что б.ч. того что в этой теме описывается, уже есть (как минимум) в Qt
    хочется publish/subscribe - signals+slots
    доставка одному конкретному адресату - invokeMethod
    из одного потока в другой - QueuedConnection
    для любителей острых ощущений в поиске дедлоков даже есть BlockingQueuedConnection, который позволяет дождаться ответа из другого потока
    передавать в сообщении можно абсолютно любые типы, не нужно никакого общего наследника, просто qRegisterMetaType + Q_DECLARE_METATYPE
    диспетчеризация по обработчикам из коробки, т.е. Qt сама вызовет нужный метод

    остается только 2 момента:
    1. невозможно запросить длину очереди сообщений (хотя маленький патч в Qt имхо может это решить)
    и в целом, асинхронные сигналы не рекомендуют использовать для интенсивных producer/consumer. Обычная очередь в мютексом будет быстрее и проще контролировать
    2. аналог линков Эрланга - при желании можно чуть пошаманить с try/catch вокруг всего event loop'a Qt, вроде никакого rocket science тоже не будет.
      Цитата Radagast @
      извините за нубский вопрос, но я всё равно не понимаю, зачем нужны эти акторы без зеленых потоков? при том, что у меня есть небольшой опыт в Эрланге, т.е. я вроде бы должен понимать, что это за акторы.
      мне кажется, что б.ч. того что в этой теме описывается, уже есть (как минимум) в Qt

      Ваш вопрос можно разделить на две части:

      * зачем нужны акторы, если большая часть их функциональности может быть обеспечена тем, что еще есть в разных фреймворках (в частности, в Qt);
      * зачем нужны акторы не на зеленых потоках.

      По первой части ответ довольно прост. Если вы разбиваете свою задачу на отдельные кусочки посредством того же Qt, вам нужно вручную управлять потоками, их запуском и остановом, привязыванием к ним тех или иных QObject-ов. А так же выбирать форму взаимодействия между ними -- где-то signal/slot, где-то invokeMethod, где-то еще что-то. А потом заменять одно на другое в процессе сопровождения. Т.е. в итоге вы все равно сделаете большую часть того, что уже лежит под капотом в SObjectizer или libcppa, но своими руками и сами затем будете это сопровождать.

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

        Цитата
        Если нет, привязывает агента к такой рабочей нити, где агент не будет никому мешать.

        Цитата
        нужно вручную управлять потоками, их запуском и остановом, привязыванием к ним тех или иных QObject-ов

        Цитата
        Если нет, привязывает агента к такой рабочей нити, где агент не будет никому мешать.

        спасибо, я всё понял
          Цитата Radagast @
          спасибо, я всё понял

          Ну, чтобы не быть голословным. Недавно делал реализацию простого теста производительности "Хамелеоны".
          Вот описание
          Вот код реализации на SObjectizer.
          Вот код аналога на Qt5 (этот код писал не я).

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

          Ну и еще в Qt-шном варианте мне не очень нравится смесь из emit-ов и postEvent-ов.
            Цитата eao197 @
            Вот код реализации на SObjectizer.

            Как-то многовато. http://play.golang.org/p/IjLcwII2Jl
              Цитата korvin @
              Как-то многовато. http://play.golang.org/p/IjLcwII2Jl


              Не удивлюсь, если на Erlang-е будет еще короче. Что это доказывает?

              Добавлено
              Цитата korvin @
              Как-то многовато. http://play.golang.org/p/IjLcwII2Jl

              Мне кажется, что вы не обеспечиваете одновременного старта всех хамелеонов. Т.е. два первых хамелеона могут завершить всю работу еще до того, как стартует третий и четвертый хамелеоны.
                Цитата eao197 @
                Ну, чтобы не быть голословным. Недавно делал реализацию простого теста производительности "Хамелеоны".
                Вот описание

                Ну, прямо скажем, не rocket science. :) На больную голову вполне решаемая задачка.
                ExpandedWrap disabled
                  #include <QCoreApplication>
                  #include <QThread>
                  #include <QMutex>
                  #include <QWaitCondition>
                   
                  #include <iostream>
                   
                  enum Color
                  {
                      Blue,
                      Red,
                      Yellow,
                      Faded
                  };
                   
                  enum Action
                  {
                      WaitForPeer,
                      WasRandevous,
                      ShouldPassBy,
                      MustDie
                  };
                   
                  class Chamelion;
                   
                  class RandevousPoint
                  {
                  public:
                      RandevousPoint(int maxRandevous)
                          : m_firstChamelion(nullptr)
                          , m_maxRandevous(maxRandevous)
                          , m_currentRandevous(0)
                      {
                          ;
                      }
                      
                      ~RandevousPoint()
                      {
                          ;
                      }
                      
                      Action ImHere(Chamelion &ch);
                      
                      void WaitForStart()
                      {
                          ;
                      }
                  private:
                      QMutex m_guard;
                      Chamelion *m_firstChamelion;
                      int m_maxRandevous;
                      int m_currentRandevous;    
                  };
                   
                  class Chamelion : public QThread
                  {
                  public:
                      Chamelion(const std::string &name, Color color, RandevousPoint &point)
                          : m_metChamelions(0)
                          , m_name(name)
                          , m_currentColor(color)
                          , m_point(point)
                      {
                          ;
                      }
                   
                      ~Chamelion()
                      {
                      }
                      
                      void SwapColors(Chamelion &other)
                      {
                          m_guard.lock();
                          std::swap(other.m_currentColor, m_currentColor);
                          m_colorChangedFlag = true;
                          m_colorChanged.wakeAll();
                          m_guard.unlock();
                      }
                   
                      void SetColor(Color color)
                      {
                          m_guard.lock();
                          m_currentColor = color;
                          m_colorChangedFlag = true;
                          m_colorChanged.wakeAll();
                          m_guard.unlock();
                      }
                      
                      int GetMetHamelions()
                      {
                          return m_metChamelions;
                      }
                      
                  private:    
                      void run()
                      {
                          m_point.WaitForStart();
                          
                          bool shouldBreak = false;
                          
                          while (!shouldBreak)
                          {
                              auto colorChanged = m_colorChangedFlag;
                              switch (m_point.ImHere(*this))
                              {
                              case Action::WaitForPeer:
                                  m_guard.lock();
                                  if (colorChanged == m_colorChangedFlag)
                                      m_colorChanged.wait(&m_guard);
                                  
                                  m_colorChangedFlag = false;
                                  m_guard.unlock();
                                  m_metChamelions ++;
                                  break;
                              case Action::WasRandevous:
                                  m_metChamelions ++;
                                  break;
                              case Action::ShouldPassBy:
                                  break;
                              case Action::MustDie:
                                  shouldBreak = true;
                                  break;
                              }
                              
                              if (!shouldBreak)
                                  ; // msleep(10);
                          }
                          // std::cout << m_name << ": Meeted chamelions = " << m_metChamelions << std::endl;
                          std::cout << "F" << std::endl;
                      }
                      
                  private:
                      int m_metChamelions;        
                      std::string m_name;    
                      Color m_currentColor;
                      RandevousPoint &m_point;
                      QMutex m_guard;
                      QWaitCondition m_colorChanged;
                      bool m_colorChangedFlag;
                  };
                   
                  Action RandevousPoint::ImHere(Chamelion &ch)
                  {
                      QMutexLocker l(&m_guard);
                      
                      if (m_currentRandevous >= m_maxRandevous)
                      {
                          ch.SetColor(Color::Faded);
                          return Action::MustDie;
                      }
                      
                      if (m_firstChamelion == nullptr)
                      {
                          m_firstChamelion = &ch;
                          return Action::WaitForPeer;        
                      }
                      
                      m_firstChamelion->SwapColors(ch);
                      m_currentRandevous ++;
                      m_firstChamelion = nullptr;
                      return Action::WasRandevous;
                  }
                   
                  int main(int argc, char *argv[])
                  {
                      QCoreApplication a(argc, argv);
                      
                      RandevousPoint point(10000);
                      {
                          Chamelion ch1("Chamelion #1", Color::Blue, point);
                          Chamelion ch2("Chamelion #2", Color::Yellow, point);
                          Chamelion ch3("Chamelion #3", Color::Red, point);
                          Chamelion ch4("Chamelion #4", Color::Blue, point);
                          
                          ch1.start();
                          ch2.start();
                          ch3.start();
                          ch4.start();
                          
                          ch1.wait();
                          ch2.wait();
                          ch3.wait();
                          ch4.wait();
                          
                          std::cout << "# of randevous: " << ch1.GetMetHamelions() + ch2.GetMetHamelions() + ch3.GetMetHamelions() + ch4.GetMetHamelions() << std::endl;
                      }
                          
                      return 0;
                  }

                UPD: Пофиксил небольшую гоночку - цвета у хамелеонов не уникальные. :)
                Сообщение отредактировано: Flex Ferrum -
                  Цитата Flex Ferrum @
                  Ну, прямо скажем, не rocket science. :) На больную голову вполне решаемая задачка.

                  Конечно не rocket science, поэтому такие задачки в качестве демок и выбираются, чтобы можно было быстро решить.
                  Кстати говоря, у вас SetColor имеет слишком сложную реализацию. Да и вообще вряд ли он нужен. Когда ImHere возвращает MustDie, хамелеон сам может поменять свой цвет на Faded на контексте своей нити, тогда никаких блокировок не нужно. Ну и код возврата ShouldPassBy, как я понял, не нужен.
                    Цитата eao197 @
                    Цитата Flex Ferrum @
                    Ну, прямо скажем, не rocket science. :) На больную голову вполне решаемая задачка.

                    Конечно не rocket science, поэтому такие задачки в качестве демок и выбираются, чтобы можно было быстро решить.
                    Кстати говоря, у вас SetColor имеет слишком сложную реализацию. Да и вообще вряд ли он нужен. Когда ImHere возвращает MustDie, хамелеон сам может поменять свой цвет на Faded на контексте своей нити, тогда никаких блокировок не нужно. Ну и код возврата ShouldPassBy, как я понял, не нужен.

                    На счёт ShouldPassBy - согласен. Просто была мысль сделать ожидание на мьютексе в ImHere с таймаутом. Тогда бы пригодилось. На счёт SetColor - в некотором смысле на автомате получилось. С одной стороны, нужды в нём действительно нет. С другой - сделано для консистентности, чтобы не размазывать логику перекрашивания хамелиона по коду точки встречи и реализации потока хамелиона. А поскольку в SetColor модифицируется защищенная мьютексом переменная, то отсюда и такая "сложность". :)

                    Добавлено
                    Тем более, что перекрашивания в Faded всегда будет происходить в контексте потока хамелиона.

                    Добавлено
                    Посмотрел вашу реализацию в заметке на Blogspot - конечно, оверкилл для этой задачи. :) Возник вот какой вопрос: а в вашей библиотеке посылать несколько раз один и тот же экземпляр сообщения можно? Как раз для таких случаев было бы полезно - разновидностей сообщений мало (порядка 12-ти), а операций с ними - очень много.

                    Добавлено
                    И зачем для сборки проекта ACE? Вы сами реакторы/проекторы не реализуете?
                      Цитата Flex Ferrum @
                      Возник вот какой вопрос: а в вашей библиотеке посылать несколько раз один и тот же экземпляр сообщения можно? Как раз для таких случаев было бы полезно - разновидностей сообщений мало (порядка 12-ти), а операций с ними - очень много.

                      Да, можно.

                      Примеры и как это делается есть тут: http://sourceforge.net/p/sobjectizer/featu...equests/3/#085c

                      С любого сообщения можно сделать make_reference (получить smart ptr), и использовать как shared_ptr. Тех тех пор, пока хотя бы один такой указатель жив, сообщение можно пересылать без копирования.
                        Цитата Flex Ferrum @
                        И зачем для сборки проекта ACE? Вы сами реакторы/проекторы не реализуете?

                        Мы где-то в 2004 или 2005 выбрали ACE в качестве базовой библиотеки для кросс-платформенности. С тех пор ее и используем, благо опыт есть и ежели чего, в ее потрохах разобраться гораздо проще, чем в Boost-е. Хотя, если бы в то время была Qt под LGPL лицензией, может быть остановились бы на Qt.

                        Сейчас в ядре SObjectizer-а из ACE используются:
                        * нити/мутексы/condition_variables;
                        * atomic-и;
                        * ACE Logging для печатей сообщений об ошибках;
                        * таймеры.

                        Заменить нити/мутексы/atomic-и уже сейчас можно на средства из С++11.
                        А вот уже с ACE Logging-ом сложнее.

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

                        Есть еще три библиотеки, построенные над ядром:
                        * so_log, которая расширяет возможности ACE Logging и упрощает логирование информации для агентов;
                        * so_5_transport, в котором задействуются ACE_Socket-ы и ACE_Reactor-ы;
                        * so_sysconf, в котором из ACE берется функциональность для работы с DLL и so-файлами.

                        Когда-то все это мы делали вручную собственными силами. Но затрахались с поддержкой тех же сокетов на разных платформах. Поэтому переход на ACE снял кучу геморроя.

                        В принципе, лично я бы хотел, чтобы со временем ядро SObjectizer-а зависело только от стандартных библиотек C++. А такие штуки, как ACE или Qt, или Boost использовались уже в дополнительных библиотеках. Но это требует времени и работы.

                        Добавлено
                        Цитата Flex Ferrum @
                        Посмотрел вашу реализацию в заметке на Blogspot - конечно, оверкилл для этой задачи.

                        Это да. Но, по большей части, из-за того, что мы для SObjectizer лаконичность кода никогда не ставили на самое первое место :)

                        С другой стороны, решение показательно тем, что пользователь нигде не видит нитей или mutex-ов.
                          eao197 по поводу ACE - ясно (хотя сотни тысяч таймеров - хмммм). Относительно примера - лучше всё-таки выбирать что-нибудь более подходящее. Т. е. из серии продьюсеров-консьюмеров. И это, старайтесь в текстах примеров избегать захвата (в контекст лямбд) чего-либо по ссылкам. Предполагается, что код лямбда будет работать за пределами захваченного контекста, и захват чего-либо по ссылкам в таком случае - это очень хороший способ наступить на грабли.
                          bsivko, угу, ясно. Интересно, насколько ускорится код eao197, если перестать постоянно дёргать хип таким вот образом?
                            Цитата Flex Ferrum @
                            eao197 по поводу ACE - ясно (хотя сотни тысяч таймеров - хмммм). Относительно примера - лучше всё-таки выбирать что-нибудь более подходящее. Т. е. из серии продьюсеров-консьюмеров. И это, старайтесь в текстах примеров избегать захвата (в контекст лямбд) чего-либо по ссылкам. Предполагается, что код лямбда будет работать за пределами захваченного контекста, и захват чего-либо по ссылкам в таком случае - это очень хороший способ наступить на грабли.

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

                            По поводу примера. Что первое в голову пришло, то и сделали. Если есть какие-то хорошие примеры для таких вещей, то я был бы рад их услышать.

                            Про захват переменных для лямбд -- все оправдано. Если говорить про пример с ping_pong-ом, из первого поста, то там pings_left захватывается по ссылке специально для того, чтобы показать, как в ad-hoc-агенте (объект которого скрыт от пользователя) можно иметь состояние, сохраняющееся между вызовами событий этого агента. Ссылку на env нужно сохранять потому, что so_environment_t -- это интерфейс с абстрактными методами, по значению его экземпляр не скопируешь, можно иметь только ссылку на него. А вот mbox-ы в агентах pinger и ponger захватываются как раз по значению и именно для того, чтобы не иметь проблем с повисшими указателями.
                              eao197, я не сомневаюсь, что в примере это оправдано. Но, как говориться, error prone. Менее опытный разработчик очень хорошо может залететь.
                                Цитата Flex Ferrum @
                                eao197, я не сомневаюсь, что в примере это оправдано. Но, как говориться, error prone. Менее опытный разработчик очень хорошо может залететь.


                                Да как по мне, так с лямбдами вообще нужно быть поосторожнее. Даже имея за плечами большой опыт разработки сложно разбираться в том, что делает код с лямбдами, когда что работает, когда что теряет актуальность, когда нужно обеспечить длительное время жизни и т.д.
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (14) « Первая ... 5 6 [7] 8 9 ...  13 14 все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0514 ]   [ 17 queries used ]   [ Generated: 18.09.25, 14:33 GMT ]