LINUX.ORG.RU

9
Всего сообщений: 37

SObjectizer-5.7.0 с поддержкой send_case в select() и so5extra-1.4.0 под BSD-лицензией

Вышли очередные версии библиотек SObjectizer и so5extra.

SObjectizer – это один из немногих все еще живых и развивающихся «акторных фреймворков» для C++ (еще есть QP/C++, CAF: C++ Actor Framework и совсем молодой еще проект rotor). Краткий обзор SObjectizer-а можно найти в этой презентации или в этой довольно старой уже статье. Хотелось бы подчеркнуть, что SObjectizer поддерживает не только модель акторов, но еще и такие модели как Publish-Subscribe и Communicating Sequential Processes. А so5extra – это набор дополнительных полезных прибамбасов для SObjectizer-а (например, реализованные на базе Asio диспетчер с пулом нитей и env_infrastructures, дополнительные типы message box-ов, средства для реализации синхронного взаимодействия и т.д.)

Если в двух словах, то:

  • SObjectizer-5.7 теперь позволяет использовать send_case в функции select(). Это делает SObjectizer-овский select() гораздо более похожим на select из Golang-а. Но это нововведение нарушило совместимость с предыдущей версией 5.6, т.к. теперь старая функция case_ стала называться receive_case;
  • в версии 5.7 устранен недочет в механизме доставки обернутых в конверты сообщений (т.е. enveloped messages) в случае использования transfer_to_state() и suppress() у агентов-получателей;
  • код so5extra теперь распространяется под BSD-3-CLAUSE лицензией, что позволяет бесплатно использовать so5extra при разработке закрытого программного обеспечения. Предыдущие версии распространялись под двойной лицензией (GNU Affero GPL v.3 и коммерческой);
  • в so5extra-1.4 реализованы mchain-ы фиксированной емкости для случаев, когда эта емкость известна на этапе компиляции.

Если же рассказывать более подробно, то основная фишка SObjectizer-5.7 – это возможность использования select() для отсылки исходящих сообщений (по аналогии с тем, как это происходит в Golang-е). Так что теперь можно делать вот такие вещи:

using namespace so_5;

struct Greetings {
   std::string msg_;
};

// Попытка отослать сообщения в соответствующие каналы,
// но все операции должны уложиться в 250ms.
select(from_all().handle_n(3).total_time(250ms),
   send_case(chAlice,
      message_holder_t<Greetings>::make("Hello, Alice!"),
      []{ std::cout << "message sent to chAlice" << std::endl; }),
   send_case(chBob,
      message_holder_t<Greetings>::make("Hello, Bob!"),
      []{ std::cout << "message sent to chBob" << std::endl; }),
   send_case(chEve,
      message_holder_t<Greeting>::make("Hello, Eve!"),
      []{ std::cout << "message sent to chEve" << std::endl; }));

В одном select() можно использовать и send_case() и receive_case() вместе. Например, вот SObjectizer-овская версия вычисления чисел Фибоначчи из в отдельном рабочем потоке (по мотивам из Golang’s tour):

using namespace std;
using namespace std::chrono_literals;
using namespace so_5;

struct quit {};

void fibonacci( mchain_t values_ch, mchain_t quit_ch )
{
   int x = 0, y = 1;
   mchain_select_result_t r;
   do
   {
      r = select(
         from_all().handle_n(1),
         // Отсылка сообщения типа 'int' со значением 'x' внутри.
         // Отсылка выполняется только когда values_ch готов для приема
         // новых исходящих сообщений.
         send_case( values_ch, message_holder_t<int>::make(x),
               [&x, &y] { // This block of code will be called after the send().
                  auto old_x = x;
                  x = y; y = old_x + y;
               } ),
         // Ожидание сообщения типа `quit` из канала quit_ch.
         receive_case( quit_ch, [](quit){} ) );
   }
   // Продолжаем операции пока что-то отсылается и ничего не прочитано.
   while( r.was_sent() && !r.was_handled() );
}

int main()
{
   wrapped_env_t sobj;

   thread fibonacci_thr;
   auto thr_joiner = auto_join( fibonacci_thr );

   // Канал для чисел Фибоначчи будет иметь ограниченный объем.
   auto values_ch = create_mchain( sobj, 1s, 1,
         mchain_props::memory_usage_t::preallocated,
         mchain_props::overflow_reaction_t::abort_app );

   auto quit_ch = create_mchain( sobj );
   auto ch_closer = auto_close_drop_content( values_ch, quit_ch );

   fibonacci_thr = thread{ fibonacci, values_ch, quit_ch };

   // Читаем первые 10 значений из values_ch.
   receive( from( values_ch ).handle_n( 10 ),
         // Отображаем каждое прочитанное значение.
         []( int v ) { cout << v << endl; } );

   send< quit >( quit_ch );
}

Полное описание нововведений версии 5.7.0 можно найти здесь.

Основное изменение в so5extra-1.4 – это смена лицензии на BSD-3-CLAUSE. Поэтому теперь все множество дополнений к SObjectizer-у из so5extra могут бесплатно использоваться в разработке закрытого коммерческого ПО.

Единственное нововведение в so5extra-1.4 – это реализация mchain для случая, когда максимальный объем mchain-а известен на этапе компиляции. Подобные mchain-ы зачастую используются в сценариях request-response, где ожидается всего одно ответное сообщение на запрос:

#include <so_5_extra/mchains/fixed_size.hpp>
#include <so_5/all.hpp>
...
using namespace so_5;

// Канал для получения ответного сообщения.
auto reply_ch = extra::mchains::fixed_size::create_mchain<1>(env,
   mchain_props::overflow_reaction_t::drop_newset);
// Отсылаем запрос.
send<SomeRequest>(target, ..., reply_ch, ...);
// Ждем и обрабатываем ответ.
receive(so_5::from(reply_ch).handle_n(1), [](const SomeReply & reply) { ... });

Надеюсь, что SObjectizer/so5extra кому-нибудь окажется полезен. Если есть вопросы, то спрашивайте, постараюсь ответить.

 , , , ,

eao197 ()

SObjectizer v.5.5.23 и so_5_extra v.1.2.0

Состоялся релиз SObjectizer-5.5.23 и so_5_extra-1.2.0. Официальный анонс здесь.

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

На базе этой фичи в so_5_extra-1.2.0 реализовано несколько новых инструментов. Так, добавлена возможность отсылки сообщений, которые можно отозвать. Например:

// Отсылаем сообщение и сохраняем ID доставки.
auto id = so_5::extra::revocable_msg::send<my_message>(mbox, ...);
... // Делаем что-то еще.
if(some_condition())
   // Решаем, что сообщение нужно отозвать.
   id.revoke(); // Если сообщение еще не дошло до получателя,
                // то оно будет отозвано и к получателю не попадет.
Отзывать можно и таймерные сообщения (т.е. отложенные и периодические). В этом случае сообщение будет отозвано даже если оно уже попало в очередь получателя (обычные таймерные сообщения в SObjectizer-е в этом случае до получателя все равно доходят).

Еще одна из новых фич so_5_extra — возможность ограничить время доставки сообщения. Например, если сообщение не доставлено до получателя за 10 секунд, то оно выбрасывается и получателю уже не доставляется. Выглядит это так:

// Создаем экземпляр сообщения, которое хотим доставить.
so_5::extra::enveloped_msg::make<check_user>(...)
   // ...теперь запаковываем его в специальный конверт...
   .envelope<so_5::extra::enveloped_msg::time_limited_delivery_t>(10s)
   // ...и отсылаем конверт с нашим сообщением..
   .send_to(target_mbox);

Взять SO-5.5.23 можно на SF.net или на GitHub-зеркале.

Взять so_5_extra-1.2.0 можно на SF.net.

Хочется поблагодарить не только тех людей, которые помогали нам с этим релизом. Но и вообще всех, кто проявлял интерес к SObjectizer-у все эти годы и находил время/возможность делится с нами своим впечатлениями, соображениями и идеями.

PS. SObjectizer-5.5 развивается уже больше четырех лет. И, вероятно, развитие ветки 5.5 подходит к своему логическому завершению. Если кому-то интересно посмотреть на то, что появилось в SO-5.5 за это время, то вот небольшой конспектик.

 , , , ,

eao197 ()

cpp_stm_free: монадическая STM библиотека для параллельного программирования

Привет,

Меня зовут Александр Гранин, и я рад представить вам свою библиотеку для Software Transactional Memory.

Software Transactional Memory (STM, программная транзакционная память) - подход к программированию многопоточных приложений с конкурентно изменяемой моделью данных. STM значительно облегчает создание многопоточного кода, так как не нужно (почти) ломать голову над синхронизацией, data races и валидностью данных. Также STM снижает риск наступить на типичные проблемы параллельного программирования: блокировки, голодание, нетривиальные многопоточные баги. STM позволяет создать две модели: безопасную конкурентную модель данных и транзакционную модель изменения этих данных.

Для С++ существует несколько STM разной степени годности: Wyatt-STM (хорошая), пропозал TS 19841:2015 (плохой), и вот теперь я создал свою библиотеку, которая называется cpp_stm_free.

Библиотека построена на продвинутых идеях из мира ФП (Free-монады, алгебраические типы данных, и др.), и имеет монадический интерфейс. Иными словами, используя эту библиотеку, вы будете писать ФП-код. Библиотека повторяет интерфейс таковой STM в Haskell: транзакции - это монадические комбинаторы в монаде STML, работающие над транзакционными переменными (TVar).

Библиотека может быть полезна в разных сценариях.

  • В серверных приложениях, обрабатывающих входящие и исходящие сообщения.
  • В soft-realtime многопоточных играх.
  • Для моделирования асинхронных вычислений.

Текущая версия библиотеки - v0.5.5. Требует GCC 7.2 и C++17 (и qmake; вскоре заменю на cmake или что-нибудь другое).

Буквально на днях я провел первый раунд оптимизаций и рефакторинга, увеличив производительность изначальной версии на 200% и добавив большое количество комбинаторов.

Для библиотеки имеется два движка, оба построены на Free монадах. Первый - Scott encoded Free monad (медленный), второй - Church encoded Free monad (быстрый). Для понимания: элементарные транзакции над простой моделью выполняются за <0.01 ms, сложные транзакции <0.1 ms, сложные транзакции в многопоточной среде <1 ms.

Более подробно с библиотекой можно ознакомиться в следующих материалах:

В качестве демонстрационного приложения я решил задачу «обедающих философов» (код здесь), и она работает хорошо. Пример транзакций для взятия ложек:

stm::STML<Unit> takeFork(const TFork& tFork) {
    return stm::withTVar<Fork, stm::Unit>(tFork, [=](const Fork& fork) {
       if (fork.state == ForkState::Free) {
           return stm::modifyTVar<Fork>(tFork, setForkTaken);
       }
       else {
           return stm::retry<stm::Unit>();
       }
    });
}

stm::STML<stm::Unit> takeForks(const TForkPair& forks) {
    stm::STML<stm::Unit> lm = takeFork(forks.left);
    stm::STML<stm::Unit> rm = takeFork(forks.right);
    return stm::sequence(lm, rm);
}

Продолжаю развивать библиотеку, на очереди - еще один раунд оптимизаций и добавление новых фич.

Буду рад ответить на вопросы.

 , , ,

GraninAS ()

SObjectizer v.5.5.22

SObjectizer — это относительно небольшой фреймворк для упрощения разработки сложных многопоточных приложений на C++. SObjectizer позволяет разработчику строить свои программы на базе асинхронного обмена сообщениями с использованием таких моделей, как Actor Model, Publish-Subscribe и CSP (в частности, каналов). Это OpenSource проект, распространяется под BSD-3-CLAUSE лицензией.

Мы выпустили очередную версию: SObjectizer-5.5.22.

Самое важное в новой версии — это возможность назначить фильтр для механизма трассировки процесса доставки сообщений (message_delivery_tracing или msg_tracing, если более коротко). Если раньше при включении msg_tracing-а SObjectizer выдавал информацию вообще обо всем, что касается доставки сообщений, что делало использование msg_tracing неудобным в больших приложениях, то теперь посредством msg_tracing-фильтров можно оставить только то, что вам интересно. Например, только информацию о сообщениях определенного типа. Или только информацию, относящуюся к конкретной рабочей нити. И т.д.

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

Еще в 5.5.22 добавлена возможность использовать свободные функции в качестве обработчиков сообщений в функциях для работы с CSP-шными каналами so_5::receive и so_5::select. И изменено поведение agent_t::so_current_state() — теперь если этот метод вызывается внутри on_enter/on_exit обработчиков, то so_current_state() возвращает ссылку на то состояние, обработчик on_enter/on_exit которого сейчас активен.

SObjectizer живет на SourceForge, есть зеркало на github. Соответственно, исходники могут быть загружены с SF.net или с github-а.

Так же мы обновили надстройку над SObjectizer-ом, библиотеку so_5_extra. so_5_extra обновилась до версии 1.1.0. Но нового в нее ничего не добавилось, только произошел переход на Asio-1.12 и SO-5.5.22. Для этого пришлось перелопатить часть библиотеки и выпустить версию 1.1.0 вместо 1.0.5. Кстати говоря, если вы использовали so_5_extra-1.0.4, то для перехода на SO-5.5.22 вам придется перейти и на so_5_extra-1.1.0, т.к. в SO-5.5.22 сломалась совместимость в той части, где идет работа с кастомными mbox-ами.

so_5_extra живет на SourceForge, взять ее можно оттуда же. Правда, распространяется so_5_extra уже под двойной лицензией.

=====

Отдельно хотелось бы отметить вот какой момент. Мы свой список хотелок для SObjectizer-а исчерпали где-то в районе версии 5.5.19 (т.е. чуть меньше года назад). С тех пор в SO-5.5 добавляются только те фичи, которые кому-нибудь понадобились. Либо нам самим, либо кому-то из пользователей.

С релизом версии 5.5.22 на этом стоит заострить особое внимание: в ветку SObjectizer-5.5 новые фичи теперь попадают только если a) они кому-то нужны и b) нас об этом просят.

Т.е. если вы хотите что-то увидеть в SObjectizer, но нам вы об этом не рассказали и мы об этом не узнали, то в ветке 5.5 вы этого точно не увидите.

=====

Было бы здорово услышать мнение тех, кто смотрел в сторону SObjectizer-а, но не выбрал его в качестве инструмента. Что остановило? Что не понравилось? Что вы не увидели в SObjectizer? Или, напротив, что такого страшного увидели?

Конструктивная обратная связь такого рода поможет нам сделать SObjectizer лучше.

 , , , ,

eao197 ()

RingBuffer для списка последних сообщений

что-то не могу найти среди всего разнообразия java контейнеров нужный для моего извращенного случая.

если вкратце то нужно что-то типа того, хотя достаточно и обычной очереди:

Collections.synchronizedMap(new LinkedHashMap<Integer, Object>()
{
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Object> eldest)
    {
       return this.size() > MAX_SIZE;   
    }
});

но

- записи будет очень много из разных потоков.

- чтения будет мало и читаться будет все целиком, те snapshot, тоже из разных потоков.

- скорость чтения не важна.

- порядок сообщений важен.

 ,

drsm ()

Хочется странного. Очереди FIFO / LIFO.

Всем привет.
Подскажите, есть ли известная и проверенная библиотека которая позволяет сделать очередь которая будет расшарена между потоками, с возможностью делать выборку либо с конца либо с начала.
P.S.
Если ничего такого нет, то возможно спонсирование разработки такой вещи с условием выпуска под свободными лицензиями.

 , ,

denisE ()

SObjectizer v.5.5.19

Сегодня мы официально выкатили очередную версию SObjectizer-а — 5.5.19. В этой версии реализованы две большие фичи, которые казались немыслимыми еще совсем недавно.

Во-первых, добавлена возможность запускать SObjectizer в однопоточном режиме. Т.е. теперь можно написать приложение на акторах так, что все акторы и вся вспомогательная кухня самого SObjectizer-а будут работать на одной единственной рабочей нити. Это может пригодиться при написании простых приложений, в которых наличие акторов может быть выгодно (для упрощения логики), а вот создание нескольких рабочих потоков — это уже оверкилл. Например, если маленькая программка должна собирать какую-то информацию и время от времени публиковать ее через MQTT. Или, скажем, при написании своей хитрой версии traceroute. Вот маленькая демонстрация того, к чему все это может прийти в пределе: тривиальный http-сервер для асинхронной обработки запросов на базе SObjectizer и restinio.

Во-вторых, добавлена возможность отсылки мутабельных сообщений. Поскольку ноги у SO-5 растут из модели Publish/Subscribe, в которой взаимодействие идет в режиме 1:N, все сообщения в SO-5 изначально были иммутабельными. В большинстве случаев это упрощало жизнь, но мешало в тех ситуациях, когда нужно было, например, построить обработку данных в режиме конвейера: один агент модифицировал данные и передавал их следующему в конвейере, при этом взаимодействие в конвейере всегда идет в режиме 1:1. В итоге в версии 5.5.19 добавлена поддержка мутабельных сообщений с обеспечением гарантии того, что мутабельное сообщение будет доставлено не более чем одному получателю. Подробнее все это показано в новой презентации из серии «Dive into SObjectizer-5.5». Кстати говоря, данная фича появилась после общения в кулуарах на C++ Russua 2017.

Все изменения в 5.5.19 описаны здесь.

Загрузить новую версию можно либо в виде архива с SourceForge, либо из svn-репозитория проекта, либо из зеркала на GitHub.

Между релизами 5.5.18 и 5.5.19 прошло довольно много времени, хотя на то были объективные причины. Надеемся, что работа над следующей версией, 5.5.20, пойдет быстрее и мы сможем выкатить ее в конце лета 2017-го.

Для будущей версии 5.5.20 у нас есть несколько своих идей, но в этом плане мы полностью открыты и готовы выслушать любые замечания и предложения. Так что, если кто-нибудь расскажет, что он хотел бы видеть в SObjectizer или, напротив, чего бы не хотел, то мы постараемся учесть это в своей работе. Опыт реализации таких фич, как отказ от дополнительного пространства имен so_5::rt, приоритеты агентов, иерархические конечные автоматы и мутабельные сообщения показывает, что это более чем возможно.

ЗЫ. Старую тему с анонсами SO-5 решил не поднимать, т.к. было это уже слишком давно. Для, кто не в курсе, что такое SObjectizer — это один из немногих живых и развивающихся OpenSource кросс-платформенных фреймворков для C++, которые дают разработчику возможность использовать Actor Model (в случае с SO-5 сюда добавляются еще и Pub/Sub, и CSP).

 , , , ,

eao197 ()

Многопоточнось, пытаюсь 2 потока сделать, как правильно?

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

class Sphere {
  vec2 position;
  vec2 size;
  float rotation;

  void onProgress() {
    ...
  }

  void onRender(Camera camera) {
    ...
  }
}

Я хочу сделать, чтобы рендеринг выполнялся в одном потоке, а onProgress в другом, т.е. onRender по сути, может только считывать данные position, size и rotation. В общем если я делаю как-то так:

class Application {
  void onProgress() {
    sphere.onProgress();
    ...
  }

  void onProgressRun() {
    while (running) {
      onProgress();
    }
  }

  void onRender() {
    sphere.onRender(camera);
    ...
  }

  void loop() {
    new Thread(&onProgressRun()).start();

    while (running) {
      ...
      onRender();
    }
  }
}

В общем это вроде бы работает, но мне кажется что так делать наверное не стоит, что произойдет, если я попытаюсь у sphere изменить position или любой другой аттрибут в основном потоке, к примеру в Application.onProgress, наверное нужно делать синхронизации? Или вообще то, каким образом я делаю не правильно? как правильнее подобную логику делать?

 , ,

Int64 ()

Правильная синхронизация set-get в Java

Юзаю findbugs для анализа написанного. Есть singleton, использование которого аналогично кэшу с небольшой предварительной обработкой, читают его все, а пишет в него poller раз в час. Поскольку читать его могут сразу несколько объектов, ставить synchronized на него как-то странно, получится узкое место (не самое крупное, но все-таки). С другой стороны, синхронизация с сеттером все-таки нужна.

сейчас сделал через ReentrantLock(), получилось так (обработку исключений, доп скобки и прочее убрал чтоб глаза не мозолить)

getter() {
  if (setterLock.isLocked())
    waittime = isSet.awaitNanos(...);
  if (waittime <= 0)
    return Collection.emptyMap();
  return instance;
}

setter(...) {
  setterLock.lock();
  try {
    ...
    if (setterLock.hasWaiters(isSet))
      isSet.signalAll();
  } finally {
    setterLock.unlock();
  }
}

findbugs ругается что мол некошерно юзать isLocked(). но другого варианта как проверить лок без собственно его захвата я что-то не нашел (tryLock() его закрывает, и надо кстати на него в сеттере поменять lock()).

 ,

upcFrost ()

Java Многопоточность, Очереди

Есть такой класс


**
 * A Combiner takes items from multiple input queues and feeds them into a
 * single output queue. Each input queue has a priority, which determines the
 * approximate frequency at which its items are added to the output queue. E.g.
 * if queue A has priority 9.5, and queue B has priority 0.5, then on average,
 * for every 100 items added to the output queue, 95 should come from queue A,
 * and 5 should come from queue B.
 * <p>
 * Input queues can be dynamically added and removed.
 * </p>
 */
public abstract class Combiner<T> {

    protected final SynchronousQueue<T> outputQueue;

    protected Combiner(SynchronousQueue<T> outputQueue) {
        this.outputQueue = outputQueue;
    }

    /**
     * Adds the given queue to this com.tech.task.Combiner.
     *
     * @param queue          the input queue to add to this com.tech.task.Combiner
     * @param priority       the priority to assign to the input queue
     * @param isEmptyTimeout if the input queue is seen as empty for a duration
     *                       longer than isEmptyTimeout, then it is removed from the com.tech.task.Combiner
     * @param timeUnit       the time unit of the isEmptyTimeout argument
     */
    public abstract void addInputQueue(BlockingQueue<T> queue, double priority,
                                       long isEmptyTimeout, TimeUnit timeUnit) throws CombinerException;

    /**
     * Removes the given queue from this com.tech.task.Combiner.
     *
     * @param queue the input queue to remove from this com.tech.task.Combiner
     */
    public abstract void removeInputQueue(BlockingQueue<T> queue) throws CombinerException;

    /**
     * Returns true if the given queue is currently an input queue to this com.tech.task.Combiner.
     */
    public abstract boolean hasInputQueue(BlockingQueue<T> queue);

    /**
     * Thrown to indicate a com.tech.task.Combiner specific exception.
     */
    public static class CombinerException extends Exception {
        public CombinerException() {
            super();
        }

        public CombinerException(String message) {
            super(message);
        }
    }
}

К нему нужно сделать реализацию. Есть идеи ?

 ,

lestal ()

Кластерный синглтон и другие примитивы потока выполнения на кластере

Есть кусок кода, он разбросан на кластер для распределения нагрузки, но есть ньюанс: только один инстанс в один момент времени может выполнять определенную работу.

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

Предлагаемое решение - кластерный синглтон. Ты делаешь класс, помечаешь его как @Singleton @Clustered, и всё происходит само. Он как-то магически поддерживается только в одном экземпляре, перезапускается на других нодах при падении, приходит к консенсусу по поводу shared mutable state если такой есть, и так далее.

Кроме синглтона существует еще куча стандартных примитивов: например, барьер (только когда N серверов вместе выполнят работу. они смогут идти дальше) или защелка с обратным отсчетом.

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

(У меня конечно тот же самый гугл и есть идеи, но всё-таки хотелось бы узнать мнение достопочтенных экспертов ЛОРа, что они уважают и любят)

 , ,

stevejobs ()

Совместное использование ресурсов

Есть проблема доступа к совместным ресурсам. Я вот не пойму, это реальная проблема, или она надумана?

Создается такое впечатление, что дезигнеры сами наплодили проблем, и затем сами же пытаются их безуспешно решить.

Пусть есть условная ячейка памяти, которую разные процессы могут инкрементить. Как принято представлять эту модель? Процесс №1 считывает текущее значение из памяти(допустим - это 0), прибавляет к нему единицу, а затем записывает в память получившееся значение вместо предыдущего. Соответственно пока процесс №1 производит операцию сложения, процесс №2 может считать значение памяти, и начать свою операцию сложения. Затем, после того как процесс №1 записал результат(1) в память, процесс № 2 также записывает свой результат(1) в память, и результат 2-х инкрементов — 1.

Отсюда проистекает необходимость введения механизмов блокировок, которые, в свою очередь порождают уже другие проблемы.

Это все выглядит уродливо, как кровавое месиво.

Давайте посмотрим на это с другой стороны.

Есть условная ячейка, к которой имеет доступ только один процесс, который умеет ее инкрементить. Остальные процессы могут послать сообщения этому процессу, с «просьбой» инкрементации. При этом, эти мессенджеры абстрагированны от текущего значения счетчика, нет нужды считывать значения и производить собственные операции над этим значением, их дело — послать собщение и все. Они не имеют никакого доступа к этой памяти.

То есть, обычная инкапсуляция. И нет никакой проблемы.

Если это так просто, то зачем городить огород? Зачем решать несуществующие проблемы? Может это выгодно всякого рода горе-дезигнерам, делать вид, что существует проблема, нет проблемы — нет и хлебушка с маслицем?

Перемещено tailgunner из development

Перемещено jollheef из job

 , ,

vikingfilmtracker ()

взаимные блокировки

Допустим для курения нужны спички и сигареты(2 ресурса). Разные процессы одновременно захотели курить, один хватает спички, другой сигареты. Далее, они взаимно ожидают освобождения другого ресурса. Это дедлок.

Но вот ведь в чем прикол. IRL это не ведет к остановке жизни на земле, эти ситуации очень просто разрешаются. Как правило, просто один процесс на время уступает ресурс другому, а потом, оба последовательно берут спички и сигареты и закуривают. Конечно, может быть и другое разрешение ситуации — мордобой:) Но он тоже не приведет к дедлоку.

Чудо? Да вроде — нет.

Тогда почему эта проблема существует в CS? И почему ее не существует в Модели Акторов?

Я вообще говоря, не знаю, врать не буду. Чтобы утверждать наверняка надо читать и изучать. Но сходу такая версия: не потому ли это происходит, что какому то из процессов в какой-то момент нужно недетерминированно отдать ресурс другому?

 , ,

portquest2016 ()

При каких нагрузках не обойтсь без использования неблокирующих подходов (использовать только потоки и процессы ОС)?

Понятно, что я не говорю о точных цифрах. Меня волнует, если проект имеет солидную нагрузку (возьмем, например, «вконтакте», «одноклассники», «твиттер») может ли он существовать (и возможно сейчас существует), только на таких подходах к многозадачности, как потоки и процессы? Естественно, в счет не берется используемые внутри проекта nginx, redis и т.п. подобные инструменты, использующие неблокирующий код. Речь только о самом приложении. Приложение в основном будет I/O bound (будем считать, что речь о веб-приложении типа социальной сети).

В университете проходим тему построения высоконагруженных систем. Хотел бы узнать у знающих людей, при каких нагрузках становиться необходимым переходить на различные неблокирующие технологии (легкие потоки, event loop-ы) и уже нельзя обходится обычными потоками операционной системы? Или же можно обойтись вообще без технологий «легких потоков» для того, чтобы строить достаточно сильно нагруженные проекты?

Я не могу понять, зачем нужен такой код, который пишется на «легких потоках». Разве это не переизобретение велосипеда, когда уже есть потоки операционной системы? Я знаю, что потоки вызывают переключения контекста, что создает накладные расходы. Но эти накладные расходы не выглядят слишком большими (максимум я видел оценку в 30 микросекунд на переключение), к тому же у переключения «легких потоков» тоже должны быть накладные расходы.

P.S. Я в принципе понимаю разницы, как я думаю, между «легкими потоками», event loop-ами и различными неблокирующими технологиями. Возможно, где-то я выражаюсь не совсем корректно. Прошу меня извинить. Надеюсь все-таки понятно о чем я говорю.

Вопрос также задан тут: https://toster.ru/q/361268

С уважением, Дмитрий

 , ,

aetolicus ()

Опрос: что бы такое запилить, чтобы там было concurrency

Я хочу наскрести на свою задницу проблем, и организовать домашний проект, чуть менее чем полностью состоящий из проблем в многопоточности Java. Там должен твориться реальный ад и свинец. Для обеспечения работоспособности себя подходят любые средства, включая модификацию openjdk.

Есть какие-нибудь идеи, что это может быть?
Желательно так, чтобы это еще и имело некую иллюзию смысла, потому что пилить совсем уж бессмысленную фигню будет муторно.

Цель: разобраться как в Java работает concurrency. Позадрачивать низкоуровневый перфоманс и байтоёбство. Как сказал один хороший человек - «писать код, много кода. Писать тесты, много тестов. Читать спеку до посинения, и труды, которые эту спеку интерпретируют».

Проблема в том, что в java web мире concurrency почти не встречается, а все проблемы с concurrency обычно решаются его решительным выпиливанием. Поэтому чтобы прошарить в теме недостаточно просто прийти на работу и собрать хлебальником все грабли - у нас их просто нет. А надо наскрести!

Что подскажет хайвмайнд?
С меня как всегда - ничего :)
Спасибо

 , ,

stevejobs ()

Обновился инструмент для работы с агентами в C++: SObjectizer 5.5.15

SObjectizer — это небольшой фреймворк для упрощения разработки многопоточных приложений на C++11 за счет использования идей из моделей акторов и publish-subscribe. SObjectizer является OpenSource-проектом и распространяется под трехпунктной BSD-лицензией.

Этот релиз добавляет возможность создания агентов в виде иерархических конечных автоматов. В версии 5.5.15 поддерживаются такие вещи, как композитные состояния, shallow- и deep-история, обработчики входа/выхода, лимиты времени, передача события на обработку в другое состояние (что-то вроде defer) и подавление событий.

Малюсенький примерчик для демонстрации новых возможностей: агент, который реализует мигающий LED-индикатор. Этот агент обрабатывает сигнал turn_on_off для включения и выключения режима мигания. Когда режим мигания включен, агент зажигает LED-индикатор на 1.5 секунды, затем тушит его на 0.75 секунды, затем опять зажигает и опять тушит и так до тех пор, пока не получит следующий сигнал turn_on_off.

Код этого агента может выглядеть вот так:

class blinking_led final : public so_5::agent_t
{
	state_t off{ this },
		blinking{ this },
		blink_on{ initial_substate_of{ blinking } },
		blink_off{ substate_of{ blinking } };

public :
	struct turn_on_off : public so_5::signal_t {};

	blinking_led( context_t ctx ) : so_5::agent_t{ ctx }
	{
		this >>= off;

		off.just_switch_to< turn_on_off >( blinking );

		blinking.just_switch_to< turn_on_off >( off );

		blink_on
			.on_enter( []{ /* some device-specific code */ } )
			.on_exit( []{ /* some device-specific code */ } )
			.time_limit( std::chrono::milliseconds{1500}, blink_off );

		blink_off
			.time_limit( std::chrono::milliseconds{750}, blink_on );
	}
};

Взять версию 5.5.15 можно либо из раздела Files на SouceForge, либо из Svn репозитория, либо из зеркала на GitHub.

 , , ,

eao197 ()

нужны ли потоки?

Раньше я как-то неправильно представлял себе абстракции параллельного выполнения. Я думал как-то так: либо у нас есть абстракция потока, либо есть акторы, которые инкапсулируют потоки. Таким образом, нам сначала нужны, например, сопрограммы, которые будут реализовывать потоки, затем уже все остальное поверх этого.

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

Чтобы иметь конкурентность, потоки вообще не нужны. И легкие нити тоже не нужны. Нам просто надо убрать, по возможности, все блокирующие операции. Если мы это сделаем, у нас уже будет конкурентное исполнение. Уже поверх этого мы можем реализовать синхронизацию событий, и тогда у нас родится абстракция потока. То есть все наоборот — сначала конкурентность, а уже поверх нее потоки. В итоге, в одном единственном ивентлупе мы имем 100-процентный параллелизм, полную абстрагированность от порядка выполнения.

 , ,

ambiguousnick ()

Обновился инструмент для работы с агентами в C++: SObjectizer 5.5.8

SObjectizer — это небольшой фреймворк для упрощения разработки многопоточных приложений на C++ за счет организации асинхронного обмена сообщениями между объектами-агентами.

Проект живет на SourceForge, распространяется под трехпунктной BSD-лицензией.

Версию 5.5.8 можно взять либо из секции Files на SF, либо из Svn-репозитория, либо из зеркала на GitHub.

Если говорить кратко, то в версии 5.5.8 появилось следующее:

  • приоритеты у агентов и три новых диспетчера, которые выполняют обработку событий с учетом приоритетов;
  • более удобные средства работы с ad-hoc агентами;
  • несколько новых примеров.

Если интересны подробности, то сюда, либо сюда, либо сюда.

Краткую информацию о том, что такое SObjectizer можно найти здесь и далее по ссылкам.

 , , ,

eao197 ()

Вопрос про потоковые редакторы UNIX

Читаю щас про sed учебник здешнего юзера, кстати, emulek'a встретил подозрительный кусок.

Далее sed последовательно читает все три файла, применяя к каждой строке в них sed-скрипт (пустой в данном случае). Результат выводится в выходной поток. Вот тут действие sed заканчивается, и опять начинает работать оболочка, последовательно записывая весь вывод в файл all.html. Потому код cat примитивен (пара строк на C), она просто читает входные файлы и выводит их в выходной поток, без всякой обработки, поисков и слияний. (Правда cat ещё и может нумеровать строки с ключом -n)

http://emulek.github.io/sed/ch01s02.html

Я, почему-то всегда думал, что выполнение происходит параллельно, или, как минимум, построчно. Типа, каждая строка передается по конвейру, а не буферы, куски etc. Может автор чуток ошибся? Или я что-то путаю?

 , , ,

quest2017 ()

std::deque data races

Добрый день. Можно ли делать так без синхронизации:

std::deque<T> d;
// thread 1
while(true){
    d.push_back(...);
}

// thread 2
while(true){
    for(size_t i = d.size() - 1;  i >= 0;  -- i){
        T &e = d[];
        ...
        // используем e
    }
}

 ,

pavlick ()