Re[4]: Вопрос по корутинам
От: B0FEE664  
Дата: 29.08.25 14:54
Оценка:
Здравствуйте, ksandro, Вы писали:

K>Есть высоконагруженный сервер. К нему каждую секунду поступает 100500 запросов. Если на каждый запрос создавать поток, а затем еще создавать поток для запроса к БД, то сервер будет тратить большую часть ресурсов исключительно на создание/убийство потоков, а так же на их синхронизацию, при этом потоки большую часть времени будут ничего не делать а просто спать ожидая ответа от бд, в итоге мы имеем очень низкую производительность трату огромных ресурсов на ничего не делание. Поэтому уже давно высокопроизводительные сервера работают несколько подругому. На самом деле с сетью работает ядро, да и с дисками работает ядро, и есть специальные системные вызовы, сигнализирующие нам о том, что какой-то в какой-то файл/сокет доступен для чтения/записи (epoll, IOCP). Да нам нужно что-то типа планировщика. В обычной ситуации мы сохраняем контекст запроса, затем можем ожидать или отправлять следующие запросы. Когда пришел ответ, мы вызываем некий коллбэк. Корутина отправляет запрос и возвращается, когда приходит ответ мы в качестве коллбека вызываем ту же самую корутину, и она продолжает работату с того самого места, где мы отправили запрос. То есть внутри корутины все выглядит как синхронная процедура, после запроса мы получаем ответ, и продолжаем работу.

K>Описал я как-то сумбурно, но вообще это сложно объяснить в двух словах. посмотрите например на примеры работы библиотеки boost asio (https://www.boost.org/doc/libs/latest/doc/html/boost_asio/examples/cpp20_examples.html#boost_asio.examples.cpp20_examples.coroutines) код с корутинами выглядит красивее. Ну и вообще погуглите про написание сетевых программ на базе корутин. Код с корутинами по скорости работает так же как неблокирующий сервер на коллбеках, накладные расходы минимальны, не сравнить с порождением потока на каждый чих. Есть еще стекфул корутины и файберы, тоже интересная штука.

Правильно ли я понимаю, что если в качестве обработчика асинхронного ответа использовать лямбду, которую положить контекст и которую через технологию Signal-Slot соединить с обработчиком, то будет тоже самое?
И каждый день — без права на ошибку...
Re: Вопрос по корутинам
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.08.25 15:21
Оценка: +1
Здравствуйте, LaptevVV, Вы писали:

Что такое yield и как он работает в C#?
и солнце б утром не вставало, когда бы не было меня
Re[12]: Вопрос по корутинам
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.08.25 15:28
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Остальное фантики. loop-fn.h — это реализация stackless корутин на plainC. Не наравится можно писать как больше нравится. На общий подход не влияет.


А можно ссылку на топик?

Чем оно лучше, чем Protothreads?
Маньяк Робокряк колесит по городу
Re[8]: Отладка многопоточности
От: landerhigh Пират  
Дата: 29.08.25 15:32
Оценка:
Здравствуйте, kov_serg, Вы писали:

L>>Господа, а что вы вкладываете в понятие "отладка (кооперативной многозадачности|многопоточности)" и почему это такая большая проблема? Для друга интересуюсь.

_>Очень просто потому как многозадачность в c++ не структурированая. Поэтому и отладка превращается в: поймай угря в ведре с угрями.

А можно с примерами? Именно отладки, а не борьбы с гонками?
www.blinnov.com
Re[13]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 29.08.25 16:28
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, kov_serg, Вы писали:


_>>Остальное фантики. loop-fn.h — это реализация stackless корутин на plainC. Не наравится можно писать как больше нравится. На общий подход не влияет.


M>А можно ссылку на топик?


На какой топик? Там файл из 20 строк.
/* loop-fn.h */
#ifndef __LOOP_FN_H__
#define __LOOP_FN_H__

typedef int loop_t;

#define LOOP_RESET(loop) { loop=0; }
#if defined(__COUNTER__) && __COUNTER__!=__COUNTER__
#define LOOP_BEGIN(loop) { enum { __loop_base=__COUNTER__ }; \
    loop_t *__loop=&(loop); __loop_switch: int __loop_rv=1; \
    switch(*__loop) { default: *__loop=0; case 0: {
#define LOOP_POINT { enum { __loop_case=__COUNTER__-__loop_base }; \
    *__loop=__loop_case; goto __loop_leave; case __loop_case:{} }
#else
#define LOOP_BEGIN(loop) {loop_t*__loop=&(loop);__loop_switch:int __loop_rv=1;\
    switch(*__loop){ default: case 0: *__loop=__LINE__; case __LINE__:{
#define LOOP_POINT { *__loop=__LINE__; goto __loop_leave; case __LINE__:{} }
#endif
#define LOOP_END { __loop_end: *__loop=-1; case -1: return 0; \
    { goto __loop_end; goto __loop_switch; /* make msvc happy */ } } \
    }} __loop_leave: return __loop_rv; }
#define LOOP_SET_RV(rv) { __loop_rv=(rv); } /* rv must be non zero */
#define LOOP_INT(n) { __loop_rv=(n); LOOP_POINT } /* interrupt n */
/* for manual labeling: enum { L01=1,L02,L03,L04 }; ... LOOP_POINT_(L02) */
#define LOOP_POINT_(name) { *__loop=name; goto __loop_leave; case name:{} }
#define LOOP_INT_(n,name) { __loop_rv=(n); LOOP_POINT_(name) }

#endif /* __LOOP_FN_H__ */


M>Чем оно лучше, чем Protothreads?


Где написано что лучше. Оно просто позволяет писать постепенно исполняемые функции на любых C-шных компиляторах в стиле coroutines.
Re[14]: Вопрос по корутинам
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.08.25 16:31
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>>>Остальное фантики. loop-fn.h — это реализация stackless корутин на plainC. Не наравится можно писать как больше нравится. На общий подход не влияет.


M>>А можно ссылку на топик?


_>На какой топик? Там файл из 20 строк.

_>
_>/* loop-fn.h */
_>


А есть примеры, как это использовать? А то самому в этой мешанине разбираться неохота


M>>Чем оно лучше, чем Protothreads?


_>Где написано что лучше. Оно просто позволяет писать постепенно исполняемые функции на любых C-шных компиляторах в стиле coroutines.


Protothreads позволяет тоже самое. Зачем было написано это?
Маньяк Робокряк колесит по городу
Re[9]: Отладка многопоточности
От: kov_serg Россия  
Дата: 29.08.25 16:41
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, kov_serg, Вы писали:


L>>>Господа, а что вы вкладываете в понятие "отладка (кооперативной многозадачности|многопоточности)" и почему это такая большая проблема? Для друга интересуюсь.

_>>Очень просто потому как многозадачность в c++ не структурированая. Поэтому и отладка превращается в: поймай угря в ведре с угрями.

L>А можно с примерами? Именно отладки, а не борьбы с гонками?

Простой пример вы вызвали функцию, она породила поток, который породил еще несколько потоков.
1. вы не знаете сколько потоков было порождено (да и сколько сейчас работает)
2. в случае убийства потока, дочерние потоки не остановятся.
3. если остановка поток требует специальной очастки типа at_thread_exits реализуется костылями (всякие ssl,tls)
4. вы не можете приостановить поток (в винде впринципе можно, но в общем случае нет)
5. вы не можете явно отслеживать переключение потоков (только косвенно).
Обычная отладка это анализ логов.
Re[15]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 29.08.25 17:29
Оценка:
Здравствуйте, Marty, Вы писали:


M>А есть примеры, как это использовать? А то самому в этой мешанине разбираться неохота

Очень просто это просто функция. Она имеет стосояние и выполняет задачу постепенно по мере передачи ей управления. О завершении сообщает с помощью кода возврата.
Код примерно такой:
int loop(state *my) {
  switch(my->loop) { default: case 0: {} // LOOP_BEGIN
    ops1;      /*LOOP_POINT ->*/ { my->loop=1; goto leave; case 1: {} }
    ops2;      /*LOOP_POINT ->*/ { my->loop=2; goto leave; case 2: {} }
    ...
    opsN;      /*LOOP_POINT ->*/ { my->loop=N; goto leave; case N: {} }
  {my->loop=-1; case -1: {}} // LOOP_END
  }
  leave: return my->loop==-1 ? 0 : 1;
}

Только с расширением для кода возврата можно прерываться не с 1, а с любым кодом с помошью LOOP_INT(interrupt_code)
Код 0 означает что функция завершила работу, 1 еще работает остальные планировшик может использовать для наращивания функционала.
В частности для задержек по времени или обработки запросов от функции

  простой пример
#include "loop-fn.h"

struct afn1_t {
  loop_t st;
  int i;
  afn1_t() { LOOP_RESET(st); }
  int loop() {
    LOOP_BEGIN(st)
    action1(); LOOP_POINT
    action2(); LOOP_POINT
    for(i=0;i<3;i++) {
      action3(); LOOP_POINT
    }
    action4(); LOOP_POINT
    LOOP_END  
  }
  void action1() {}
  void action2() {}
  void action3() {}
  void action4() {}
};

void usage() {  afn1_t afn; while(afn()) {} }
  с delay
#include "loop-fn.h"

#define DELAY(n) LOOP_INT(-n)

struct Play1 {
  loop_t st;
  const char* text;
  Play1() { reset(); }
  void reset() { LOOP_RESET(st); text=""; }
  int loop() {
    LOOP_BEGIN(st)
    text="Hello"; DELAY(1000);
    text="world"; DELAY(1000);
    text="";
    LOOP_END
  }
};

void usage() {
  Play1 play;
  for(;;) {
    int rc=play.loop();
    if (rc==0) break;
    if (rc<0) delay(-rc);
  } 
}
  с запросами к супервизору
#include "loop-fn.h"
enum { lrcChunkReady=3, lrcRequest=4 };
#define LOOP_DELAY(n)   LOOP_INT(-n)
#define LOOP_REQUEST() LOOP_INT(lrcRequest)

struct Request {
  enum Command { Cmd1, Cmd2, Cmd3 };
  enum Status { None, Requested, Success, Failed };
  int cmd,status;
  bool supported() { return status!=Requested; }
  void cmd1() { cmd=Cmd1; status=Requested; }
  int result;
};

struct Afn2_t {
  loop_t st;
  int result;
  Request *req;
  Afn2_t() { LOOP_RESET(st); }
  int loop() {
    LOOP_BEGIN(st)
       req->cmd1(); LOOP_REQUEST
       if (!req->supported()) { result=-1; LOOP_INT(0); }
       result=req->result;      
    LOOP_END()
  }
};

void simple_supervisor() {
  Anf2_t afn[1];
  Request req[1];
  afn->req=req;
  for(;;) {
    int rc=afn->loop(); if (rc==0) break;
    if (rc==lrcRequest && req->status==Request::Requested) { // handle request
      req->result==42;
      req->status=Request::Success;
    }
    if (rc==lrcChunk) { /* handle next chunk */ }
    if (rc<0) delay(-rc);
  }
}

M>>>Чем оно лучше, чем Protothreads?

Не лучше просто подход другой.
Предпологается что любая асинхронная функция описывается структурой
typedef struct afn_t {
  void *ctx;
  int (*setup)(void *ctx,int sev);
  int (*loop)(void* ctx);
} afn_t;
enum SetupEvents { sevInit, sevDone };
enum LoopReturnCodes { lrcDone, lrcWorking };
  в случае c++ тоже тривиально приводится к такому виду
struct A {
  virtual int setup(int sev) { return 0; }
  virtual int loop() { return 0; }

  static A* my(void *ctx) { return (A*)ctx; }
  static int _loop(void* ctx) { return my(ctx)->loop(); }
  static int _setup(void* ctx,int sev) { return my(ctx)->setup(sev); }
  operator afn_t () { afn_t afn; afn.ctx=this; afn.setup=_setup; afn.loop=_loop; return afn; }
};

Любая асинхронная функция может получать события (event) через метод setup и производить работу когда ей явно передадут упраление через loop()
При этом для инициализации используется sevInit а для освобождения ресурсов событие sevDone. Код возврата 0 — значит успешно, остальное нет
Гарантируется что если был вызван sevInit то потом будет вызван sevDone.
После sevInit можно вызывать loop который сообщит что он закончил если вернёт 0. Он выполняет задачу порциями так чтобы не зависать на долго.
Если возвращает 1 то он еще работает и требует еще кванта выполнения, остальные коды возврата зависят от планировщика.
Например числа <0 можно использовать для sleep(timeout) а коды 2,3,4... для других прерываний/запросов
Например фунцкия может сообщить что очередная порция данных готова, или запросить данные из внешних источников и только после их получений или timeout-та возобновить исполнение.
Сами планировщики отдельно.

_>>Где написано что лучше. Оно просто позволяет писать постепенно исполняемые функции на любых C-шных компиляторах в стиле coroutines.

M>Protothreads позволяет тоже самое. Зачем было написано это?
protothread и loop-fn.h похожи но решают разные совершенно задачи.
afn_t нужна для декомпозиции асинхронных задач и получения структурированной конкуренции, а так же для возможности сохранять состояния постепенно исполняемых функций.
Но в основном задача была сделать максимально просто. loop-fn просто побочный файл позволяющий писать код в стиле корутин на любом C компиляторе для максимального охвата.
Re[7]: Вопрос по корутинам
От: ksandro Мухосранск  
Дата: 29.08.25 20:57
Оценка:
Здравствуйте, so5team, Вы писали:

S>А зачем?

S>Прелесть stackfull-короутин в том, что ты пишешь обычный код (как при классическом многопоточном программировании).
даже проще чем в многопоточном, в большинстве случаев можно не париться о синхронизации.

S>А моменты переключения делаются автоматически под капотом функций, которые могут заблокировать выполнение (например, read/write/accept/listen и т.п.).


Да, тут согласен. Просто мой небольшой опыт использования stackfull корутин на практике показал, что оберток над всеми функциями ввода вывода, которые нужны нету. Пришлось самому писать эти обертки, вызывающие yield(). А это не всегда так просто как кажется на первый взгляд. То есть я довольно много времени потратил на написание кода специфичного именно для корутин. Но да, вя бизнес логика уместиласть в одну функцию, и ее можно было даже для отладки запускаать как синхронную блокирующую.
Re[10]: Отладка многопоточности
От: landerhigh Пират  
Дата: 29.08.25 21:28
Оценка:
Здравствуйте, kov_serg, Вы писали:

L>>А можно с примерами? Именно отладки, а не борьбы с гонками?

_>Простой пример вы вызвали функцию, она породила поток, который породил еще несколько потоков.
_>1. вы не знаете сколько потоков было порождено (да и сколько сейчас работает)

обычно таки знаем. Более того, в нашей тихой гавани нужно точно знать, сколько всего есть потоков. Потому что isolation, affinity и прочие страшные слова.

_>2. в случае убийства потока, дочерние потоки не остановятся.


Стараемся рисовать такую архитектуру, где удается обойтись без геноцида работающих потоков. А если поток сам роскомнадзорнулся, то это уже авария. Самый правильный вариант — let it crash, и желательно прям сразу, чтобы адекватный coredump получить.

_>3. если остановка поток требует специальной очастки типа at_thread_exits реализуется костылями (всякие ssl,tls)


Бывает. Решается.

_>4. вы не можете приостановить поток (в винде впринципе можно, но в общем случае нет)


Вот ни разу не было необходимости.

_>5. вы не можете явно отслеживать переключение потоков (только косвенно).


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

_>Обычная отладка это анализ логов.


www.blinnov.com
Re: Вопрос по корутинам
От: fk0 Россия https://fk0.name
Дата: 30.08.25 10:03
Оценка: 1 (1) +1
Здравствуйте, LaptevVV, Вы писали:

LVV>А в каких задачах корутины вот прям супер — супер?

LVV>Чего раньше приходилось делать муторно и долго ?

Ни в каких. Они могут быть удобны в задачах где нужны конечные автоматы
или событийное программирование. Они позволяют писать как будто бы императивный
код. Проблема в том, что в конечном счёте для разработки автоматы -- лучше.
Проблема импертивного программирования в задачах логического управления,
чтоб собственно логика не проектируется отдельно от программирования (кодирования),
а рождается программистом по ходу дела из головы. И в любом более-менее сложном
случае реализуемая логическая функция оказывается не полноценной (логические ошибки).
Для автоматов можно хотя бы выполнить простейшие проверки, что все предусмотренные
состояния достижимы, что нет тупиковых состояний из которых нет выхода, что
во всех возможных состояниях предусмотрена реакция на все возможные входные события.
Можно с помощью систем проверки моделей проверить, что реализуемый конечный автомат
удовлетворяет заданным проверочным условиям во всём множестве его возможных состояний
и в любой комбинации поступающих входных событий. Это дорогого стоит. Альтернатива
в виде тестирования лишь покрывает узкий набор сценариев.

Простейшая иллюстрация того, о чём я говорю, многие функции ожидания событий
в императивном программировании ожидают ровно ОДНО событие (иначе при вызове
такой функции расписывать switch-case на два десятка вариантов...) И такие
алгоритмы прекрасно работают когда события происходят в определённом, предусмотренном
прогрммистом порядке. А что если порядок не детерминированный (а программист
этого не предусмотрел). Событийное программирование снимает этот недостаток,
но привносит описанные выше проблемы: состояние теперь не выделено явным образом
(в императивной программе оно определяется счётчиком программных инструкций),
и программист вообще затрудняется сказать в скольки состояниях может находиться
программа и в скольки из них какие события и как обрабатываются. Состояние
закодировано в очередях сообщений, в факте подписки на определённые события,
в отдельных переменных. Его можно выделить в отдельную единую переменную и тогда
получится ДКА, с которым легко работать. А как его программировать именно дело
десятое. Хоть методом им. Шалыто, хоть в рамках событийной системы с очередями
сообщений.

В последнем случае есть ещё одна опасность: рекурсии и ёмкость очередей
сообщений, которые фактически уподобляются стеку. Очевидно, рекурсивный алгоритм
на очередях сообщений ведёт к их переполнению, если глубина рекурсии не ограничена
и программистом не делались её оценки. Алгоритм может быть даже не рекурсивным.
Просто положим, для всех событий используется единая очередь сообщений. И однажды
в неё попадает входное событие, в процессе обработки которого порождается несколько
синтетических сообщений, которые тоже попадают в очереь для обработки. И возникает
своеобразная лавина событий, превышающая размер очереди. Как доказать, что очереди
хватит всегда, спрашивается. Ответом на этот вопрос может служить не очередь,
а что-то вроде множества (std::set), где события имеют атомарный характер:
они происходили в прошлом или нет, но не говорится сколько раз или с какими
параметрами -- эта информация должна передаваться через отдельные и различные
механизмы, вроде fifo-очередей специфичных для событий определённого типа.
Например, переполнение буфера клавиатуры для системы в целом может быть не критично.
А если обезьяна нажимала кнопки, породила описанную выше лавину событий переполнившую
единственную глобальную очередь событий -- это катастрофа.

Возвращаясь к корутинам -- они могут быть встроенны в событийный механизм,
чтоб там вручную не выписывать switch-case для простых автоматов с несложной
логикой, где переключение между состояниями преимущественно линейное.
Re[10]: Отладка многопоточности
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 30.08.25 17:09
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, landerhigh, Вы писали:


L>>Здравствуйте, kov_serg, Вы писали:


L>>>>Господа, а что вы вкладываете в понятие "отладка (кооперативной многозадачности|многопоточности)" и почему это такая большая проблема? Для друга интересуюсь.

_>>>Очень просто потому как многозадачность в c++ не структурированая. Поэтому и отладка превращается в: поймай угря в ведре с угрями.

L>>А можно с примерами? Именно отладки, а не борьбы с гонками?

_>Простой пример вы вызвали функцию, она породила поток, который породил еще несколько потоков.
_>1. вы не знаете сколько потоков было порождено (да и сколько сейчас работает)
_>2. в случае убийства потока, дочерние потоки не остановятся.

В .Net для async/await используется пул потоков. Но можно вызвать LongRunning для создания потока
Асинхронное программирование на основе задач

Библиотека параллельных задач (TPL) основана на концепции задачи, представляющей асинхронную операцию. В некотором смысле задача напоминает поток или ThreadPool рабочий элемент, но на более высоком уровне абстракции. Термин параллелизм задач относится к одной или нескольким независимым задачам, выполняемым одновременно. Задачи предоставляют два основных преимущества:

Более эффективное и более масштабируемое использование системных ресурсов.

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

Более высокий уровень управления программой, чем это возможно с помощью потока или рабочего элемента.

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

По обеим причинам TPL является предпочтительным API для написания многопоточных, асинхронных и параллельных кодов в .NET.



Параметры создания задачи
и солнце б утром не вставало, когда бы не было меня
Re[5]: Вопрос по корутинам
От: ksandro Мухосранск  
Дата: 01.09.25 21:45
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Правильно ли я понимаю, что если в качестве обработчика асинхронного ответа использовать лямбду, которую положить контекст и которую через технологию Signal-Slot соединить с обработчиком, то будет тоже самое?


А... не очень понимаю, о чем ты. Могу точно сказать, что нужную функциональность получить конечно же можно. Вопрос в читаемости кода. Тут фишка, в том, что после обработки ассинхронного ответа ты можешь продолжить выполнение функции с места, где ты отправлял запрос, и выглядить снаружи это будет почти также как быдто запрос был синхронным. И часто это значительно выглядит значительно яснее, чем разбираться в множестве лямбд, обраюотчиков коллбеэков сигналов и слотов.
Re[7]: Отладка многопоточности
От: ksandro Мухосранск  
Дата: 02.09.25 09:38
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, ksandro, Вы писали:


K>>Ну, код все-таки не совсем был бы аналогичным, вместо всех этих co_ все равно нужно вызывать yield() везде, где хочешь отдать управление. Отладка кооперативной многозадачности тоже ооочень веселый и увлекательный процесс, как бы она ни была реализована, хотя в любом случае проще чем отлаживать multi-threading.


L>Господа, а что вы вкладываете в понятие "отладка (кооперативной многозадачности|многопоточности)" и почему это такая большая проблема? Для друга интересуюсь.


Многопоточность — это вытесняющая многозадачность, ассинхронность в одном потоке (коллбэки или корутины) — это кооперативная многозадачность. Корутина сама отдает управление другой корутине, потом та отдает управлеие обратно и тд.

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

Но в отличии в случае многопоточности у нас вообще бесконечное число способов прийти в данное состояние, плюс любая операция записи может нафиг разрушить целостность памяти. И нет никакого способа проверить корректность.
Re[8]: Отладка многопоточности
От: kov_serg Россия  
Дата: 02.09.25 10:07
Оценка:
Здравствуйте, ksandro, Вы писали:

K>Отладка многозадачности сложна, потому, что у нас много одновременно выполняющихся задач (да, я понимаю что я капитан очевидность). И у нас вместо одного понятного текущего состояния программы, в которое мы можем прийти некоторым конечным числом способов, появляется состояние, состоящее из текущего состояние каждой задачи, прийти в это самое состояние мы можем в общем огромным количеством способов. И отследить как мы попали в таое состояние бывает очень и очень непросто.


K>Но в отличии в случае многопоточности у нас вообще бесконечное число способов прийти в данное состояние, плюс любая операция записи может нафиг разрушить целостность памяти. И нет никакого способа проверить корректность.


Вот поэтому должны быть явно описаны требования, гарантии и инварианты. И желатьельно что бы их было мало и они были простые. Иначе будет бардак и "никакого способа проверить корректность".
Re[9]: Отладка многопоточности
От: ksandro Мухосранск  
Дата: 02.09.25 12:51
Оценка: +2
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, ksandro, Вы писали:


K>>Отладка многозадачности сложна, потому, что у нас много одновременно выполняющихся задач (да, я понимаю что я капитан очевидность). И у нас вместо одного понятного текущего состояния программы, в которое мы можем прийти некоторым конечным числом способов, появляется состояние, состоящее из текущего состояние каждой задачи, прийти в это самое состояние мы можем в общем огромным количеством способов. И отследить как мы попали в таое состояние бывает очень и очень непросто.


K>>Но в отличии в случае многопоточности у нас вообще бесконечное число способов прийти в данное состояние, плюс любая операция записи может нафиг разрушить целостность памяти. И нет никакого способа проверить корректность.


_>Вот поэтому должны быть явно описаны требования, гарантии и инварианты. И желатьельно что бы их было мало и они были простые. Иначе будет бардак и "никакого способа проверить корректность".


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

З.Ы. Был как-то реальный случай, убрали одну ненужную строчку логирования, которая давно была в коде и всем мешала. Прогнали тесты все ок, да и какие проблемы может вызвать такое мелкое изменение. Но оказалось, что в коде много лет сидел race condition, эта никому не нужная запись в лог давала задержку в несколько микро или даже нано секунд, благодаря этому ошибка не проявлялась, а в продакшене вдруг стала периодически ни с того ни с сего вылезать. И это был код, который много лет проработал в продакшене, и был давно отлажен и протестирован.
Отредактировано 02.09.2025 19:22 ksandro . Предыдущая версия .
Re[10]: Отладка многопоточности
От: rg45 СССР  
Дата: 02.09.25 13:10
Оценка:
Здравствуйте, ksandro, Вы писали:

K>З.Ы. Был как-то реальный случай, убрали одну ненужную строчку логирования, которая давно была в коде и всем мешала. Прогнали тесты все ок, да и какие проблемы может вызвать такое мелкое изменение. Но оказалось, что в коде много лет сидел race condition, эта никому не нужная запись в лог давала задержку в несколько микро или даже нано секунд, благодаря этому ошибка не проявлялась, а в продакшене вдруг стала периодически ни с того ни с сего вылезать.


А я как-то (давно уже) пытался отловить race conditions при помощи отладочной печати Вот именно это и происходило — отладочная печать вносила синхронизацию и проблема переставала воспроизводиться.
--
Справедливость выше закона. А человечность выше справедливости.
Re[11]: Отладка многопоточности
От: landerhigh Пират  
Дата: 03.09.25 20:43
Оценка:
Здравствуйте, rg45, Вы писали:

R>А я как-то (давно уже) пытался отловить race conditions при помощи отладочной печати Вот именно это и происходило — отладочная печать вносила синхронизацию и проблема переставала воспроизводиться.


Классика!
www.blinnov.com
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.