На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Распараллеливание выполнения без блокировок
    Всем привет!

    Продолжаю тему своего Кардридера. Решил, как уже говорил ранее, опрос Кардридера выполнять отдельной нитью. Но практики параллельного программирования у меня практически нет, и я убил пару дней на изучение материала, чтение книжек и примеров из инета.

    Вот получилась такая у меня прога. Пример чисто синтетический. Одна нить чего-то генерирует (в данном случае std::string), кладет генерируемое в разделяемый стек. А основная нить программы опрашивает стек, и если видит хотя бы один элемент - забирает его из стека и печатает содержимое того, чего забрала.

    Сама прога:
    Скрытый текст
    ExpandedWrap disabled
      #include <functional>
      #include <iostream>
      #include <string>
      #include <memory>
      #include <atomic>
      #include <chrono>
      #include <thread>
      #include <ctime>
       
      #if defined(_WIN32)
      #include <conio.h>
      #elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
      #include <cstdio>
      #include <sys/select.h>
      #include <sys/ioctl.h>
      #include <termios.h>
       
      int _kbhit() {
        static const int STDIN = 0;
        static bool initialized = false;
        if (!initialized) {
          termios term;
          tcgetattr(STDIN, &term);
          term.c_lflag &= ~(ICANON | ECHO);
          tcsetattr(STDIN, TCSANOW, &term);
          setbuf(stdin, NULL);
          initialized = true;
        }
        int bytesWaiting;
        ioctl(STDIN, FIONREAD, &bytesWaiting);
        return bytesWaiting;
      }
      #else
      #error "На тостере не работаю!"
      #endif
       
      using namespace std::chrono_literals;
       
      template <typename T>
      struct Node {
        std::shared_ptr<T> Data;
        Node* Next;
        Node(T const& iData): Data(std::make_shared<T>(iData)) {  }
      };
       
      template <typename T>
      class FreeStack {
          std::atomic<Node<T>*> Head;
        public:
          void Push(T const& iData) {
            Node<T>* const NewNode = new Node<T>(iData);
            NewNode->Next = Head.load();
            while (!Head.compare_exchange_weak(NewNode->Next, NewNode));
          }
          std::shared_ptr<T> Pop() {
            Node<T>* OldHead = Head.load();
            while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next));
            return OldHead ? OldHead->Data : std::shared_ptr<T>();
          }
      };
       
      class WriterThread {
          std::atomic_bool& RunFlag;
          std::atomic_bool& DoneFlag;
          FreeStack<std::string>& Stack;
          WriterThread() = delete;
        public:
          explicit WriterThread(std::atomic_bool& iRunFlag, std::atomic_bool& iDoneFlag, FreeStack<std::string>& iStack):
            RunFlag(iRunFlag), DoneFlag(iDoneFlag), Stack(iStack) {}
          void operator()() const {
            int cnt = 0;
            std::srand(unsigned(std::time(0)));
            while (RunFlag) {
              cnt++;
              uint32_t Sleep = 500 + (std::rand() % 1000);
              std::this_thread::sleep_for(std::chrono::milliseconds(Sleep));
              std::string Tmp = std::to_string(cnt) + " : " + std::to_string(Sleep) + " : writer string";
              Stack.Push(Tmp);
            }
            DoneFlag = true;
            std::cout << "Writer is over..." << std:: endl;
          }
      };
       
      class ReaderThread {
          std::atomic_bool RunFlag = true;
          std::atomic_bool DoneFlag = false;
          FreeStack<std::string> Stack;
        public:
          ReaderThread() {}
          void Run() {
            WriterThread Writer(RunFlag, DoneFlag, Stack);
            std::thread Thread(Writer);
            Thread.detach();
            while (!_kbhit()) {
              std::shared_ptr<std::string> Tmp = Stack.Pop();
              if (Tmp) {
                std::cout << *(Tmp.get());
                std::cout << std::endl;
              }
            }
            std::cout << "A key was pressed..." << std:: endl;
            RunFlag = false;
            while (!DoneFlag) {
              std::cout << "Waiting for a completion of the detached thread..." << std:: endl;
              std::this_thread::sleep_for(std::chrono::milliseconds(50));
            }
          }
      };
       
      int main() {
        ReaderThread Reader;
        Reader.Run();
        std::cout << std::endl;
        std::cout << "Bye-bye!" << std::endl;
        return 0;
      }

    А вот примерный вывод после работы проги:
    Скрытый текст
    ExpandedWrap disabled
      1 : 591 : writer string
      2 : 634 : writer string
      3 : 827 : writer string
      4 : 751 : writer string
      5 : 954 : writer string
      6 : 947 : writer string
      7 : 815 : writer string
      8 : 625 : writer string
      9 : 642 : writer string
      A key was pressed...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Writer is over...
       
      Bye-bye!

    Собственно прошу критики.

    1) Где какие ошибки я допустил?
    2) В каких местах имеет смысл ловить и обрабатывать исключения?
    3) По скольку отдельная нить не заточена на вычисления, а скорее на ожидания - какой интервал опроса имеет смысл оставить, чтобы попусту не грузить систему?

    А я пока посмотрю, как обстоят дела с QThread. Читал, вроде там сигналы-слоты реализованы потоко-безопасно. Может вообще не придется городить разделяемый стек, а данные передавать прямо в сигнале.

    Добавлено
    Цитата JoeUser @
    1) Где какие ошибки я допустил?

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

    И точно - я не ошибся !!! :)

    Вот новый код метода ReaderThread::Run()

    ExpandedWrap disabled
      void Run() {
        WriterThread Writer(RunFlag, DoneFlag, Stack);
        std::thread Thread(Writer);
        Thread.detach();
        while (true) {
          if (_kbhit() && RunFlag) {
            std::cout << "A key was pressed..." << std:: endl;
            RunFlag = false;
          }
          if (!RunFlag && !DoneFlag)
            std::cout << "Waiting for a completion of the detached thread..." << std:: endl;
          std::shared_ptr<std::string> Tmp = Stack.Pop();
          if (Tmp) {
            std::cout << *(Tmp.get());
            std::cout << std::endl;
          } else {
            if (DoneFlag)
              break;
          }
        }
      }

    А вот вывод "с дочисткой стека":
    ExpandedWrap disabled
      1 : 1352 : writer string
      2 : 1042 : writer string
      3 : 582 : writer string
      4 : 595 : writer string
      5 : 1203 : writer string
      6 : 1347 : writer string
      7 : 1483 : writer string
      A key was pressed...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Waiting for a completion of the detached thread...
      Writer is over...
      8 : 625 : writer string
       
      Bye-bye!
      Цитата
      Распараллеливание выполнения без блокировок

      Сразу забудьте. Это не Ваш случай.

      По поводу QT проблемы с глобальными сиглтонами из-за параллельной сборки он в исполняемые файлы пихает несколько штук. Пришлось через разделяемую память делать свой синглтон и на основе него мьютексы для блокировок.
      Сообщение отредактировано: Pavia -
        Цитата Pavia @
        Сразу забудьте. Это не Ваш случай.

        Так не пойдет! Или обоснование и объяснение, или сей коммент - в трэш.

        Цитата Pavia @
        По поводу QT проблемы с глобальными сиглтонами из-за параллельной сборки он в исполняемые файлы пихает несколько штук.

        Pavia, ты что издеваешься?! >:( Компиляция может быть параллельной (есть ключи типа -jN), но сборка (окончательная линковка) тут при чем? Более того ... "Глобальные синглтоны" чего??? И почему не может быть несколько разных синглтонов? И тут же ... или обоснования и объяснения, или сей коммент - в трэш.

        M
        Я сразу обозначу свою позицию. Если обоснований не будет - я расценю твой коммент как типа "на поржать" над новичком.
        А если это будет так - впилю форумное наказание по максимуму. Если это не де жа вю, то подобного качества коменты от
        тебя на моей памяти уже второй раз. Тебе сутки!
          M
          Не поленился и поискал тот коммент, который был де жа вю  - вот он.
          Который так и остался "пшиком" без объяснения и аргументации.
            JoeUser
            Цитата JoeUser @
            ты что издеваешься?! Компиляция может быть параллельной (есть ключи типа -jN), но сборка (окончательная линковка) тут при чем?

            Я вам рассказал про подводные камни. А что с ними делать это вам решать.

            Цитата
            "Глобальные синглтоны" чего??? И почему не может быть несколько разных синглтонов?

            Синглтон в переводи на русский одиночка. Он по своей природе один. А вот интерфейсов у него может быть много. Короче RTFM.

            Цитата
            Так не пойдет! Или обоснование и объяснение, или сей коммент - в трэш.

            А что обосновывать? Атомарные операция это тоже блокировка 50-180 тактов в зависимости от процессора. И также приводит LiveLock(разновидность DeadLock)
              Цитата Pavia @
              А что обосновывать? Атомарные операция это тоже блокировка 50-180 тактов в зависимости от процессора.

              Атомарные операции в трактовке С++, операции "без блокировки". Что творится на уровне команд процессора/ОС - это уже раздел уровнем ниже, типа "ASM". Но мы общаемся в терминах Стандарта С++ и его терминологии.

              Цитата Pavia @
              Сразу забудьте. Это не Ваш случай.

              На это утверждение так и нет обоснования! Жду.

              Цитата Pavia @
              Синглтон в переводи на русский одиночка. Он по своей природе один. А вот интерфейсов у него может быть много. Короче RTFM.

              Только ненадо RTFM'ами кидаться. Я прекрасно знаю что такое Синглтон. Я просто прошу обосновать, а желательно и привести пример по вот этому утверждению:

              Цитата Pavia @
              По поводу QT проблемы с глобальными сиглтонами из-за параллельной сборки он в исполняемые файлы пихает несколько штук.

              Опять жду обоснования и пример.

              Цитата Pavia @
              Я вам рассказал про подводные камни.

              Пока только голые утверждения, нет ни конкретики, ни примеров!

              Добавлено
              Цитата Pavia @
              И также приводит LiveLock(разновидность DeadLock)

              Кстати ... и тут пруф какбэ нужен! Иначе это из разряда догадок.

              Добавлено
              Цитата Pavia @
              И также приводит LiveLock

              Теоретически да - в 0.00000000000001% случаев, когда две операции выполняют однотипные по тактам операции. Но когда один поток зависит от готовности "периферии", а второй просто чекает флаг - это утверждение приобретает ценность размышлений о жизнедеятельности сферического коня в вакууме!
                Цитата JoeUser @
                1) Где какие ошибки я допустил?

                1) Вместо std::thread - используй std::future в связке с async (это следует делать в 95% случаях, по крайней мере так советует Майерс, и я ему верю, хотя некоторые не согласны с этим утверждением, но все же) - потому что std::thread имеют кучу подводных камней(читай Майерса)
                2) Вместо std::atomic<ololo> flag - используй conditial variables.
                3) Тебе точно нужен вот этот стек, может лучше очередь ? В любом случае твой стек выглядит весьма странно, какие то циклы внутри, я так понимаю это что то типа примитивных неблокирующих средств синхронизации? Я бы юзал всеже conditional_variables, они туда больше подходят.

                Какой стандарт плюсов ты используешь? В С++17 завезли, ЕМНИП, алгоритмы для паралельных вычислений и возможно многопоточные структуры данных, я б изучил сперва, что есть в языке.

                Цитата JoeUser @
                2) В каких местах имеет смысл ловить и обрабатывать исключения?

                В твоем случае необходимо юзать std::future и почитать как обрабатывать исключения из него(У Майерса все это есть.)

                Цитата JoeUser @
                3) По скольку отдельная нить не заточена на вычисления, а скорее на ожидания - какой интервал опроса имеет смысл оставить, чтобы попусту не грузить систему?

                Юзать conditional variable. Ну и вообще я бы на твоем месте почитал про схему Producer -> Consumer, в многопоточном программировании. У тебя же один Consumer ? Тогда можно одной conditional_variable обойтись.

                Ну и еще я бы почитал на твоем месте, хотя бы по диагонали Майерса(Скот Майерс "Эффективный и современный C++") и книгу про многопоточность в последнем стандарте. Но из последнего в принципе подойдет и "Энтони Уильямс - Параллельное программирование на С++ в действии."

                Добавлено
                Цитата JoeUser @
                Вот новый код метода ReaderThread::Run()

                Если что, все вот эти std::cout - глобальные переменные, а не просто абы что. Я к тому, что обычно такие вещи пишутся с блокировками.
                Сообщение отредактировано: Wound -
                  Wound, мы с тобою как будто из разных вселенных! :lol:

                  Я код не просто из башки взял. Я изучил книгу Энтони Уильямса "Прараллельное программирование в действии". И "структуры без блокировок" взял оттуда, почти один-в-один, ну или почти, только мои "расширения", скажем так. Нахрена мне другая модель параллелизма, к примеру с std::future? Тем более std::future - это ожидание результата асинхронной операции. Но это не мой случай. У меня нет результата асинхронной операции! У меня есть просто асинхронное исполнение второго потока, результаты которого нужно юзать время от времени (по факту готовности).

                  Цитата Wound @
                  Вместо std::atomic<ololo> flag - используй conditial variables.

                  А вот тут не вижу профита совсем. Если у меня неблокируемое ожидание, чем мне поможет std::condition_variable? Да вообще ничем! Будет просто другой синтаксис, но отдача квантов вычисленя CPU при ожидании останется равной. Так нахрена коту баян?

                  Киля, я внимательно прочел твой пост. И понял одно - ты меня хочешь притянуть за уши к блокировкам на основе мьютексов. Но, поверь, это не мой вариант. Это и первое, что мне пришло в голову. Но я специально изучил главу "Структуры, свободные от блокировок", чтобы обойтись без локов в угоду атомарным операциям.

                  Давай договоримся так, ты указываешь мне как надо делать в моем коде в плане исправления ошибок (или просто где ошибки), но не указываешь как изменять архитектуру кода. Не учишь меня жизни сейчас (я сам когда-то если и обломаюсь, то научусь). А я тебе скидываю скан книги Энтони Уильямса "С++ Concurrency in Action" в русском переводе. Только скажи как и куда?

                  Скрытый текст
                  Энтони Уильямс. Параллельное программирование в действии. Практика разработки многопоточных программ. Пер с англ.Слинкин A.A. - M: ДМК Пресс, 2012.-627c.:ил. ISBN 978-5-94074-448-1
                    Цитата Wound @
                    Если что, все вот эти std::cout - глобальные переменные, а не просто абы что. Я к тому, что обычно такие вещи пишутся с блокировками.

                    Хм :-? ... Принято!!! Пруф?
                      Цитата Wound @
                      Но из последнего в принципе подойдет и "Энтони Уильямс - Параллельное программирование на С++ в действии."

                      :yummy:

                      Все мои нынешние "знания" - исключительно из этой книги. И про "структуры, свободные от блокировок" - тож все оттуда. Я ее перечитал вдоль и поперек, и до сих пор перечитываю. Оч толковый автор, который не просто толкает тему, но и останавливается на мелочах. Важных мелочах. А как говорят "Диавол кроется в мелочах". Вот там то мы его найдем и прихлопнем мухобойкой. Ибо нехрен!

                      Добавлено
                      Цитата Wound @
                      кучу подводных камней

                      Если не сложно, просто перечисли. А я сравню со своей ситуацией, коснется ли меня оно.
                        Цитата JoeUser @
                        Я код не просто из башки взял. Я изучил книгу Энтони Уильямса "Прараллельное программирование в действии". И "структуры без блокировок" взял оттуда, почти один-в-один, ну или почти, только мои "расширения", скажем так.

                        Ну да и на пустом месте нафигачил кучу какого то непонятного кода. ИМХО, тут можно сделать более понятно безо всяких флагов.

                        Цитата JoeUser @
                        Нахрена мне другая модель параллелизма, к примеру с std::future? Тем более std::future - это ожидание результата асинхронной операции. Но это не мой случай. У меня нет результата асинхронной операции! У меня есть просто асинхронное исполнение второго потока, результаты которого нужно юзать время от времени (по факту готовности)

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

                        Цитата JoeUser @
                        А вот тут не вижу профита совсем. Если у меня неблокируемое ожидание, чем мне поможет std::condition_variable? Да вообще ничем! Будет просто другой синтаксис, но отдача квантов вычисленя CPU при ожидании останется равной. Так нахрена коту баян?

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

                        Цитата JoeUser @
                        Киля, я внимательно прочел твой пост. И понял одно - ты меня хочешь притянуть за уши к блокировкам на основе мьютексов. Но, поверь, это не мой вариант. Это и первое, что мне пришло в голову. Но я специально изучил главу "Структуры, свободные от блокировок", чтобы обойтись без локов в угоду атомарным операциям.

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

                        Цитата JoeUser @
                        Давай договоримся так, ты указываешь мне как надо делать в моем коде, а указываешь что у меня в потенциале, в моем коде, должно глючить (где и почему). Но не учишь меня жизни сейчас (я сам когда-то если и обломаюсь, то научусь). А я тебе скидываю скан книги Энтони Уильямса "С++ Concurrency in Action" в русском переводе. Только скажи как и куда?

                        В принципе можешь делать как хочешь. Но я бы на твоем месте все же Майерса почитал. Например:
                        Цитата

                        Подход на основе задач обычно превосходит свой аналог на основе потоков, и небольшие
                        фрагменты кода, с которыми вы встретились выше, показывают некоторые причины этого.
                        Здесь doAsyncwork дает возвращаемое значение, в котором, как мы можем разумно предположить, заинтересован вызывающий doAsyncwork код. В случае вызова на основе потоков
                        нет простого способа к нему обратиться. При подходе на основе потоков нет простого способа получить доступ к вызову. В случае же подхода на основе задач это можно легко сделать,
                        поскольку фьючерс, возвращаемый std : : async, предлагает функцию get. Эта функция get
                        еще более важа, если doAsyncwork генерирует искючение, поскольку get обеспечивает доступ и к нему. При подходе на основе потоков в случае генераии функцией doAsyncWork
                        исключения програма аварийно завершается (с помощью вызова std: : terminate).
                        Более фундаментаьным различием между подходаи на основе потоков и на основе задач является воплощение более высокого уровня абстракции в последнем. Он освобождает
                        вас от деталей уравления потоками, что, кстати, напомнило мне о необходимости рассказать о трех значениях слова поток (tread) в параллельном программировании на С++

                        У std::thread - есть косяки с join/detach, по хорошему его нужно оборачивать в класс. Возможно это не твой случай, но все же. Исключения, которые ты не поймал внутри потока - ты больше нигде и никак не сможешь обработать - утечка ресурсов. Используешь ты их примитивно, потому как использование std::thread - обосновано, когда ты хочешь полностью контролировать работу потока вручную! Ты же ее в своем коде никак не контролируешь от слова совсем, даже приоритет не выставляешь.

                        Цитата JoeUser @
                        Хм ... Принято!!! Пруф?

                        Пруф чего именно?
                        Допустим, вот в этом куске кода:
                        Цитата JoeUser @
                        std::cout << "Waiting for a completion of the detached thread..." << std:: endl;

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

                        Добавлено
                        Цитата JoeUser @
                        Если не сложно, просто перечисли. А я сравню со своей ситуацией, коснется ли меня оно.

                        Я тебе перечислю ситуации, когда следует использовать std::thread, так понятней наверно будет, вот из книги Майерса:
                        Цитата

                        Тем не менее существуют ситуации, в которых применение потоков
                        может быть более подходящим. Они включают в себя следующее.
                        • Вам нужен доступ к API, лежащей в основе реализации потоков. В С++ API паралельных вычислений обычно реализуется с помощью низкоуровневого платформозависимого API, обычно pthreads или Windows Threads. Эти API в настоящее время
                        предлагают больше возможностей, чем С++. (Например, С++ не имеет понятий приоритетов или родственности потоков.) Для предоставления достуа к API реализации
                        потоков std : : thread обычно предагает функцию-член nat ive_handle. Така функциональность отсутствует в std : : future (т.е. в том, что возвращает std : : async).
                        • Вам требуется возможность оптимизации потоков в вашем приложении. Это может произойти, наример, если вы разрабатываете серверно программное обеспечение с известным профилем выполнения, которое может быть развернуто как единственный процесс на машине с фиксированными аппаратными характеристиками.
                        • Вам требуется реализовать поточную техолоrию, выходящую за рамки API параллельных вычислений в С++, например пулы потоков на платформах, на которых ваши реализации С++ их не предоставляют.
                        Однако это нестандартные ситуации. В большинстве случаев вы должны выбирать
                        программирование на основе задач, а не на основе потоков.
                        Сообщение отредактировано: Wound -
                          А зачем самому lock-free алгоритмы писать? Поверь, это либо просто и с кучей подводных камней, либо сложно и нетривиально. Куда лучше взять какой-нибудь libcds и юзать lock-free хоть очередь хоть стек хоть map.

                          Добавлено
                          Цитата Wound @
                          Упростит немного код и избавит от лишних флагов, которые только запутывают

                          У него изначальное ТЗ - сделать без блокировок, так что cv пролетает мимо.
                            Цитата OpenGL @
                            У него изначальное ТЗ - сделать без блокировок, так что cv пролетает мимо.

                            Да это у него не тз такое, это он сам себе втемяшил что надо делать именно так и никак иначе. А теперь говорит - не учи меня писать, я обчитался книг и теперь хочу писать именно так и никак иначе. :-?

                              censored
                              M
                              Q: оффтоп, JoeUser. К чему тут этот переход на личность?

                              Цитата Wound @
                              Плюс ко всему избежать косяков detach/join у потока

                              What??? :blink: Где, покажи в коде!!!

                              Цитата Wound @
                              на самом деле происходит не атомарный вывод на консоль

                              Киля, ну это же говно!!!
                              Вместо std::cout << "Waiting for a completion of the detached thread..." << std:: endl; я могу написать std::cout << "Waiting for a completion of the detached thread...\n"; это такая мелочь для синтетического примера. Хотя, да, ты тут прав.

                              Цитата Wound @
                              Цитата
                              Однако это нестандартные ситуации. В большинстве случаев вы должны выбирать
                              программирование на основе задач, а не на основе потоков.

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

                              Добавлено
                              Цитата OpenGL @
                              А зачем самому lock-free алгоритмы писать?

                              А у меня тривиальный стек - так почему бы и нет?
                              Или лучше обложиться мьютексами ... так сказать - для надежности? :lol:

                              Добавлено
                              Цитата Wound @
                              Да это у него не тз такое, это он сам себе втемяшил что надо делать именно так и никак иначе. А теперь говорит - не учи меня писать, я обчитался книг и теперь хочу писать именно так и никак иначе.

                              Киля, при всем уважении, ты не лучше всегда знаешь что я хочу.
                              Иногда я хочу лучше чем ты можешь за меня хотеть! 8-)

                              Добавлено
                              Цитата Wound @
                              втемяшил что надо делать именно так и никак иначе

                              Да, и кстати, анука обоснуй ... если есть возможность реализовать структуры без блокировок, зачем "мне твое иначе" (с блокировками)? Обоснуй профит!
                              Сообщение отредактировано: Qraizer -

                                offtop was censored
                                Цитата JoeUser @
                                What??? Где, покажи в коде!!!

                                Ну ты ж отцов то читать не хочешь. Мне тебе сюда всю книгу цитировать или как?

                                M
                                Q: читать отцов – это хорошо. Но ради синтетического простого примера тратить время на гугление материалов, их освоение и осознания идей и концепций никто ожидаемо не будет. Если хочешь заинтересовать оппонента, не надо отправлять в поиск не_знаю_куда, с его точки зрения, не_знаю_чего не_знаю_зачем_мне_якобы_нужного.


                                Цитата JoeUser @
                                Киля, ну это же говно!!!
                                Вместо std::cout << "Waiting for a completion of the detached thread..." << std:: endl; я могу написать std::cout << "Waiting for a completion of the detached thread...\n"; это такая мелочь для синтетического примера. Хотя, да, ты тут прав.

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

                                Цитата JoeUser @
                                Да, и кстати, анука обоснуй ... если есть возможность реализовать структуры без блокировок, зачем "мне твое иначе" (с блокировками)? Обоснуй профит!

                                С блокировками оно надежнее, особенно когда у тебя есть расшаренный ресурс. Сегодня у тебя один продьюсер и один потребитель. Завтра может быть несколько потребителей, а после завтра - появятся несколько продьюсеров и несколько потребителей. И будешь ты переписывать свой код от начала и до конца, либо мудохаться с lock-free флагами как у тебя.
                                Сообщение отредактировано: Qraizer -

                                  offsop was censored
                                  Цитата Wound @
                                  Ну ты ж отцов то читать не хочешь. Мне тебе сюда всю книгу цитировать или как?

                                  Да нет, я желаю на каждый "профессиональный взгляд" иметь кроме бла-бла-бла - что-то подтверждающее. Ну напиши мне строку в моем коде, обоснуй ошибку. Так уже третья моя просьба мимо. Спрашивается, а нахрена твои слова, если по существу не можешь указать где плохо и как лучше? Извини за эмоции.

                                  Цитата Wound @
                                  С блокировками оно надежнее, особенно когда у тебя есть расшаренный ресурс

                                  Бинго! Вот этого ДЖВА года ждал! :lol: А еще надежнее - записывать параллельно в бумажный журнал.

                                  Цитата Wound @
                                  Сегодня у тебя один продьюсер и один потребитель. Завтра может быть несколько потребителей, а после завтра - появятся несколько продьюсеров и несколько потребителей.

                                  А ты почитай-почитай про lock-free структуры. Когда прочитаешь, прочитай еще раз. И только потом ты поймешь, что то, что я процитировал тебя выше - лютая дичь от незнания. Я читаю и учусь, и ты читай.
                                  Сообщение отредактировано: Qraizer -
                                    Цитата Wound @
                                    С блокировками оно надежнее

                                    Этот аргумент я себе запишу в смартфон!!! :lol: :lol: :lol: Это не должно умереть!

                                      offtop was censored
                                      Цитата JoeUser @
                                      Да нет, я желаю на каждый "профессиональный взгляд" иметь кроме бла-бла-бла - что-то подтверждающее. Ну напиши мне строку в моем коде, обоснуй ошибку. Так уже третья моя просьба мимо. Спрашивается, а нахрена твои слова, если по существу не можешь указать где плохо и как лучше? Извини за эмоции.

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

                                      Цитата JoeUser @
                                      Бинго! Вот этого ДЖВА года ждал! А еще надежнее - записывать параллельно в бумажный журнал.

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

                                      Цитата JoeUser @
                                      А ты почитай-почитай про lock-free структуры. Когда прочитаешь, прочитай еще раз. И только потом ты поймешь, что то, что я процитировал тебя выше - лютая дичь от незнания. Я читаю и учусь, и ты читай.

                                      Ну конечно. Ты даже постом выше уже признал что std::cout у тебя не потокобезопасен.

                                      Цитата JoeUser @
                                      Этот аргумент я себе запишу в смартфон!!! Это не должно умереть!

                                      Конечно запиши, и желательно еще сделай обои на рабочий стол с этим текстом, чтоб не забыть.

                                      offtop was censored
                                      Сообщение отредактировано: Qraizer -
                                        Цитата JoeUser @
                                        Этот аргумент я себе запишу в смартфон!!! Это не должно умереть!

                                        А так оно и есть. С блокировками у тебя всегда есть части кода, гарантированно выполняющиеся одним потоком. Это банально проще отлаживать и куда быстрее писать, чем сидеть и представлять "а вот тут у меня будет переключение контекста, сюда вклинится другой поток и выполнит воон ту фигню" с lock-free. Так что да, решение с блокировками наверняка окажется надёжнее из-за своей простоты.
                                          offtop was censored

                                          Цитата Wound @
                                          Я тебе уже привел хренову тучу аргументов - отказаться от std::thread, тебе этого мало?

                                          Прости, я что-то кроме бла-бла-бла не заметил сути. Перечисли, пожалуйста (ПОЖАЛУЙСТА)!!! еще раз по пунктам. Чем тебе плох std::thread?

                                          Цитата Wound @
                                          потому что у тебя нет блокировок и гарантировать что эта операция потокобезопасная - нельзя

                                          Т.е как я тебя понимаю, если в Стандарте написано, что данная операция из текущей версии STL атомарная - тебе насрать?! Ты этому не доверяешь?

                                          Цитата Wound @
                                          Ты даже постом выше уже признал что std::cout у тебя не потокобезопасен.

                                          Не "даже" а "всего лишь" - ощути разницу! А ты из говно-момента делаешь себе корону. Хотя нет, мелочей нет. Ты тут прав. Это есть косяк и он должен быть запомнен на будущее.

                                          Цитата Wound @
                                          как Pavia, за то что не привел тебе пруфы

                                          А вот на зло тебе и ему не выдам! :lol: Он не просто ограничился "тезисом", а пытался обосновать. А ты, как всегда, с шашкой на-голо в бой. Нихрена не понял, но по ходу разберемся :lol: Киля, не парься - ты на форуме есть, и это клёво! Даже если мы стреляемся из разных барикад :)
                                          Сообщение отредактировано: Qraizer -
                                            Цитата OpenGL @
                                            А так оно и есть. С блокировками у тебя всегда есть части кода, гарантированно выполняющиеся одним потоком. Это банально проще отлаживать и куда быстрее писать, чем сидеть и представлять "а вот тут у меня будет переключение контекста, сюда вклинится другой поток и выполнит воон ту фигню" с lock-free. Так что да, решение с блокировками наверняка окажется надёжнее из-за своей простоты.

                                            А мне не надо "надежнее надежного". Мне надо чтобы инварианты из STL выполнялись. И если там есть часть функций/методов которые работают с lock-free структурами, я буду ими пользоваться. Нравится вам это или нет.

                                            Я вас спросил в первом посте - есть ошибки? А вы меня грузите опасениями!!! Мне этого не надо, я это не просил !!!
                                              Тут интересное по теме - Параллельное программирование на С++ в действии.Уильямс
                                              В том числе и по lock-free структурам.
                                                Цитата JoeUser @
                                                Мне надо чтобы инварианты из STL выполнялись.

                                                Какие инварианты и как их нарушает решение с блокировкой?

                                                Цитата JoeUser @
                                                Я вас спросил в первом посте - есть ошибки?

                                                Так я и не спорю. Хочешь делать нерационально - дело твоё, мне-то что?
                                                  Цитата OpenGL @
                                                  Какие инварианты и как их нарушает решение с блокировкой?

                                                  "Перпендикулярный" вопрос ващето! Я про атомарные операции библиотеки... Они и гарантируют инварианты.
                                                  "Решения с блокировкой" - это вами навязываемая шляпа, к которой я в данном треде никакого отношения не имею.

                                                  Цитата OpenGL @
                                                  Хочешь делать нерационально - дело твоё, мне-то что?

                                                  Вот пошла тема!!! Рациональность. Прекрасненько. Тут есть о чем поговорить. Расскажи мне, пожалуйста, как сделать рациональнее!
                                                  Вводная: есть GUI процесс, есть процесс, опрашивающий Кардридер в неблокирующем режиме.
                                                  В моем синтетическом примере основной процесс = ReaderThread, а процесс опроса Кардридера WriteThread.

                                                  Жду более "оптимальный" вариант и конечно же его обоснование!
                                                    Цитата JoeUser @
                                                    Я про атомарные операции библиотеки... Они и гарантируют инварианты.

                                                    Тогда ты просто сам не понял, что сказал, потому что в этом контексте фраза "чтобы инварианты из STL выполнялись." теряет смысл.

                                                    Цитата JoeUser @
                                                    Вводная: есть GUI процесс, есть процесс, опрашивающий Кардридер в неблокирующем режиме.

                                                    У тебя там lock-free контейнеры при межпроцессном взаимодействии? :D Мсье знает толк, однако. Чем тупое решение с блокировкой не устроило-то?

                                                    Добавлено
                                                    По-моему тут что-то из разряда "если в руках молоток, то всё кажется гвоздями"
                                                      Цитата OpenGL @
                                                      Тогда ты просто сам не понял, что сказал, потому что в этом контексте фраза "чтобы инварианты из STL выполнялись." теряет смысл.

                                                      :blink:

                                                      Не понял тебя. Атомарные операции гарантируют "единоличный" доступ/операции. Считывание, обмен... Ты о каком смысле вообще?

                                                      Цитата OpenGL @
                                                      Мсье знает толк, однако. Чем тупое решение с блокировкой не устроило-то?

                                                      Мусье, а вы бы мне могли обосновать необходимость блокировки, где и без нее можно обойтись?
                                                      И еще, мусье, вы немножко знакомы с тезисом Уильяма из Оккамы ("Бритва Оккамы"), и если "да",
                                                      спросите себя - нахрена городить огород? Я имею ввиду- блокировками, если и без них все
                                                      прекрасно?
                                                        Цитата JoeUser @
                                                        Не понял тебя. Атомарные операции гарантируют "единоличный" доступ/операции. Считывание, обмен... Ты о каком смысле вообще?

                                                        Ну перечитай ещё раз :-? Разжёвывать очевидную мысль, да ещё оффтопя, мне не интересно.

                                                        Цитата JoeUser @
                                                        Мусье, а вы бы мне могли обосновать необходимость блокировки, где и без нее можно обойтись?

                                                        Звучит примерно как "обоснуй необходимость компилятора, ведь можно в hex кодах всё написать". Можно, конечно, просто это сложно. lock free алгоритмы в разы более сложные в написании и отладке, да ещё и далеко не всегда они работают быстрее (по-моему очередь и стек так вообще часто медленней оказываются, чем с блокировкой), так что в данном случае всё наоборот - ты должен обосновать необходимость реализовывать с lock-free. У тебя есть замеры времени в реальных условиях, из которых следует, что ты много времени тратишь на на ожидания в мьютексах? Или может быть твоя основная цель это научиться работать с lock-free? Если ответа на оба вопроса "нет", то твоё решение менее рационально, чем решение с блокировками.
                                                        Сообщение отредактировано: OpenGL -
                                                          Цитата OpenGL @
                                                          Разжёвывать очевидную мысль, да ещё оффтопя, мне не интересно.

                                                          Я немного гиперболизирую, несколько в обход вежливому общению. Просто представь, ты пишешь утверждение типа:
                                                          censored.
                                                          M
                                                          Вы с OpenGL не поняли друг друга. Это не повод обижаться и раскультуриваться. Я понял каждого, хоть и не с первого раза. Вы оба правы, но говорите о разных предметных областях.


                                                          Цитата OpenGL @
                                                          Звучит примерно как "обоснуй необходимость компилятора, ведь можно в hex кодах всё написать".

                                                          Что мой FreeStack так серьезно сложен? Да ладно? Да ладно!
                                                          Есть другое мнение. Для компиляции Хэллоу Ворлда не нужно ставить lcc (в 624KB), а нужно ставить вcю студию в более чем 11GB. Зачем?
                                                          ДА НА ВСЯКИЙ СЛУЧАЙ !!! Прекрасная логика!

                                                          ПРЕСНАЯ ВОДА ПО ПРЕЖНЕМУ detected!
                                                          В первой теме код и вопросы. Это тема.
                                                          Сообщение отредактировано: Qraizer -
                                                            Цитата JoeUser @
                                                            Вводная: есть GUI процесс, есть процесс, опрашивающий Кардридер в неблокирующем режиме.
                                                            В моем синтетическом примере основной процесс = ReaderThread, а процесс опроса Кардридера WriteThread.

                                                            Жду более "оптимальный" вариант и конечно же его обоснование!

                                                            Не хотелось бы встревать в жёсткую дискуссию..что-то я устал
                                                            за последнее время.
                                                            Могу просто рассказать, как делал сам.
                                                            А в качестве обоснования могу привести наисерьёзнейший аргумент:
                                                            "Нутром чую, что правильно - именно так".
                                                            Скрытый текст

                                                            Итак, есть GUI - приложение с его оконной процедурой.
                                                            Есть рабочий поток (или потоки) которые что-то там много делают.
                                                            Пусть будет карт-ридер, это неважно. Хоть 10.

                                                            1. Смастерим класс-коммуникационный объект, для работы и обмена между
                                                            разными потоками. Это будет шаблонный класс.
                                                            Его основа - две потоко-безопасные очереди. Разного направления,
                                                            условно "туда и обратно". Где хочешь их бери, хоть сам луди.
                                                            (а что такое очередь? Это массив, индексы которого рассчитываются по алгоритму очереди)

                                                            2. Напишем класс-интерфейс для возбуждения событий.
                                                            Назовём его, например, I_RiseEvent. У него будет роутина RiseEvent, которая будет
                                                            реализовываться в дочерних классах.

                                                            3. Класс нашего главного окна (или любого другого, которого надо)
                                                            назначим наследником - того библиотечного класса окна и интерфейса I_RiseEvent.
                                                            Множественным наследованием.

                                                            4. Тоже самое с нашим рабочим потоком - он наследник базового класса - потока и I_RiseEvent.

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

                                                            6. как всё работает - допустим юзер щёлкнул по кнопке - обработчик кнопки заполнил
                                                            структуру с запросом, занёс её в коммуникационный объект в направлении "туда".
                                                            Сам факт занесения вызвал процедуру "RiseEvent" у объекта-потока.
                                                            Эта процедура подожгла семафор.
                                                            Семафор разбудил поток, который мирно спал на Wait-процедуре.
                                                            Поток запустил обработчик работы/опроса чего надо. Допустим, карт-ридера.
                                                            Через некоторое время (а главному окну всё это пофигу) операция завершилась.
                                                            Как-то. Обработчик потока записал результаты в какую-то там (заранее изобретённую)
                                                            структуру и заслал в коммуникационный объект в очередь "обратно". Сам факт записи в очередь
                                                            вызвал процедуру "RisEvent" уже главного окна. Чтобы не работать в контексте
                                                            рабочего потока, окно пошлёт само себе асинхронное, заранее предусмотренное
                                                            сообщение.
                                                            Получив сообщение, обработчик окна этого сообщения извлечёт из очереди структуру
                                                            с результатом... и у юзера в строке статуса (например) появится сообщение -
                                                            "Операция такая-то завершилась успешно"


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

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

                                                            Привожу пример - получаем по сети данные и строим график.
                                                            1-й рабочий поток непосредственно работает с сетью, получает данные,
                                                            проверяет, преобразовывает, засылает в комм-объект, засыпает.
                                                            2-й рабочий поток-графопостроитель. Получает данные 1-го, просыпается,
                                                            начинает рисовать, результат засылает в комм-объект (другой уже) засыпает.
                                                            3-й поток - главного окна. Возбуждение события приведёт к извлечению картинки
                                                            и перерисовке ею главного окна.
                                                            Однако, графопостроитель - приёмник запросов не от одного комм-обекта.
                                                            Он должен получать данные от главного окна :
                                                            - при измерении размеров окна
                                                            - при изменении графических параметров - цветА, фонты, вид гафика, их количество итд.
                                                            Все, что можно назвать "формой представления".
                                                            И поток-графопостроитель действительно будет выполнять все эти запросы от 3-х источников
                                                            последовательно и слать результат одному приёмнику (в виде картинки главному потоку).

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

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

                                                            Как-то так.
                                                              Цитата JoeUser @
                                                              Что мой FreeStack так серьезно сложен? Да ладно? Да ладно!

                                                              Да. Он не сложнее быстрой сортировки, например, но если кто-то будет писать сортировку просто так вместо использования той, что в стандартной библиотеке, мотивируя это тем, что она несложная, то это кто-то просто решает задачу нерационально. Как и ты сейчас.
                                                                Цитата JoeUser @
                                                                Что мой FreeStack так серьезно сложен? Да ладно? Да ладно!

                                                                Конечно сложен, ты в нем сам даже толком не разобрался :lol:
                                                                Допустим на вскидку:
                                                                ExpandedWrap disabled
                                                                  void Push(T const& iData) {
                                                                        Node<T>* const NewNode = new Node<T>(iData);
                                                                        NewNode->Next = Head.load();
                                                                        while (!Head.compare_exchange_weak(NewNode->Next, NewNode));
                                                                      }
                                                                      std::shared_ptr<T> Pop() {
                                                                        Node<T>* OldHead = Head.load();
                                                                        while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next));
                                                                        return OldHead ? OldHead->Data : std::shared_ptr<T>();
                                                                      }

                                                                Чему будет равно значение переменной OldHead в Pop'e если стек пустой?
                                                                Вот тут я имею ввиду:
                                                                ExpandedWrap disabled
                                                                   Node<T>* OldHead = Head.load(); //! - вот эта шляпа что тебе возвратит, если стек твой пуст?
                                                                   
                                                                  while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next)); //! А тут что будет, если тебе возвратит отличное от 0 значение?

                                                                Ты с указателями бы разобрался для начала, а уж потом бы lock-free алгоритмы писал.
                                                                Сообщение отредактировано: Wound -
                                                                  ЫукпШ, конечно большое спасибо за ответ!
                                                                  Но, увы, слишком много букв, слишком много ненужных возможностей.
                                                                  А моя цель проста, никаких библиотечных реализаций, все - тупо для проекта.
                                                                  Никаких "расширений" - только 2 потока, строго 2 потока.

                                                                  Возникает вопрос, зачем мне все выше прочитанное?
                                                                  Возникает второй вопрос, а был ли прочтен вместе с кодом заданный вопрос с первом сообщении?
                                                                    Цитата JoeUser @
                                                                    Возникает вопрос, зачем мне все выше прочитанное?

                                                                    Всё просто.
                                                                    Если возникает такой вопрос, значит и читать не надо.

                                                                    Добавлено
                                                                    Цитата JoeUser @
                                                                    А моя цель проста, никаких библиотечных реализаций, все - тупо для проекта.
                                                                    Никаких "расширений" - только 2 потока, строго 2 потока.

                                                                    Это только кажется.
                                                                    Завтра к тебе придут и скажут:
                                                                    - Ты так классно сделал работу !
                                                                    Нам понравилось, делай ещё много таких проектов, хороших и нужных.
                                                                      Цитата Wound @
                                                                      Чему будет равно значение переменной OldHead в Pop'e если стек пустой?

                                                                      Если стек не пуст - pop() вернет последний OldHead->Data или пустой std::shared_ptr<T>(), если стек пуст;

                                                                      Цитата Wound @
                                                                      Конечно сложен, ты в нем сам даже толком не разобрался

                                                                      Т.е я как-то написал, он работает, и я не разобрался, и ты кросава? :lol:
                                                                      Зашибись логика! :jokingly:

                                                                      Добавлено
                                                                      Скрытый текст
                                                                      Цитата ЫукпШ @
                                                                      Завтра к тебе придут и скажут:

                                                                      Это вообще оффтоп. Но не скажут - щя получилось так, я являюсь и Заказчиком И Исполнителем.
                                                                        Цитата JoeUser @
                                                                        Скрытый текст
                                                                        Цитата ЫукпШ @
                                                                        Завтра к тебе придут и скажут:

                                                                        Это вообще оффтоп. Но не скажут - щя получилось так, я являюсь и Заказчиком И Исполнителем.

                                                                        Скрытый текст

                                                                        ...
                                                                        На предварительном следствии в полицейском участке на все вопросы он вопил одну и ту же стереотипную фразу:
                                                                        — У меня писчебумажный магазин!
                                                                        На что получал такой же стереотипный ответ:
                                                                        — Это для вас не оправдание.
                                                                        ...
                                                                        Сообщение отредактировано: ЫукпШ -
                                                                          ЫукпШ, у меня лишь одно оправдание - я не стараюсь предугадывать будущее, а стараюсь оперативно решать текущие вопросы. И решать максимально быстро. Другой вопрос "а качественно ли?". Стараюсь качественно. Но приоритет - сроки, потом качество. И да, грешен, иногда приходится говнокодить.

                                                                          Но по теме ... не представляю, зачем на компе 2 и более одинаковых кард-считывателя. У меня моск разрушается!
                                                                            Цитата JoeUser @
                                                                            Если стек не пуст - pop() вернет последний OldHead->Data или пустой std::shared_ptr<T>(), если стек пуст;

                                                                            Давно у shared_ptr метод load появился?


                                                                            Цитата JoeUser @
                                                                            .е я как-то написал, он работает, и я не разобрался, и ты кросава?
                                                                            Зашибись логика!

                                                                            Тебе только кажется что оно работает.
                                                                              Цитата Wound @
                                                                              Давно у shared_ptr метод load появился?

                                                                              Это метод std::atomic ващето!censored

                                                                              Добавлено
                                                                              Цитата Wound @
                                                                              Тебе только кажется что оно работает.

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

                                                                              Но, по-моему, ты считаешь себя умнее Энтони Уильямса.
                                                                              При всем моем уважении к тебе - я уверен, ты себя
                                                                              переоцениваешь.
                                                                              Сообщение отредактировано: Qraizer -
                                                                                Цитата JoeUser @
                                                                                Это метод std::atomic ващето! Ты чего там? ку-ку??

                                                                                Да неужели. Перечитай ещё раз мой пост выше. Я даже тебе комментарии к проблемным участкам написал
                                                                                  Цитата JoeUser @
                                                                                  Но по теме ... не представляю, зачем на компе 2 и более одинаковых кард-считывателя. У меня моск разрушается!

                                                                                  Это легко !
                                                                                  Скрытый текст

                                                                                  Очень люблю почитать.
                                                                                  У меня в электронной книге стоит карточка с личной подборкой - очень много
                                                                                  уже, гигабайт 10 скопилось.
                                                                                  Понадобилось/захотелось сбросить копию на другую карточку.
                                                                                  Вставил 2 карт-ридера и скопировал с одной карты на другую.
                                                                                  Сообщение отредактировано: ЫукпШ -
                                                                                    Добрался до компа, давай еще раз:

                                                                                    Цитата JoeUser @
                                                                                    Если стек не пуст - pop() вернет последний OldHead->Data или пустой std::shared_ptr<T>(), если стек пуст;

                                                                                    Причем тут что вернет pop?
                                                                                    Я спрашивал:
                                                                                    Цитата Wound @
                                                                                    Чему будет равно значение переменной OldHead в Pop'e если стек пустой?


                                                                                    Теперь на пальцах:
                                                                                    ExpandedWrap disabled
                                                                                      template <typename T>
                                                                                      class FreeStack {
                                                                                          std::atomic<Node<T>*> Head; //! <<< Голый указатель обернутый в std::atomic
                                                                                        public:
                                                                                          void Push(T const& iData) {
                                                                                            Node<T>* const NewNode = new Node<T>(iData); //! <<< Выделение памяти
                                                                                            NewNode->Next = Head.load(); //! <<<< Что тут вернет load, если стек пустой???? nullptr ??? Ты уверен в этом? :D
                                                                                            while (!Head.compare_exchange_weak(NewNode->Next, NewNode));
                                                                                          }
                                                                                          std::shared_ptr<T> Pop() {
                                                                                            Node<T>* OldHead = Head.load(); //! <<<< Что тут вернет load, если стек пустой ???? nullptr ??? Ты уверен в этом? :D
                                                                                            while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next)); //!  <<< Тут идет проверка что если OldHead не nullptr - что то сделать,
                                                                                      //! в частности если память для OldHead выделена не была(а вызов pop легко может быть вызвон до push), и при этом OldHead не равен nullptr, Грохнется вот тут -> OldHead->Next <<< Access Violation
                                                                                            return OldHead ? OldHead->Data : std::shared_ptr<T>();
                                                                                          }
                                                                                      };

                                                                                    Так понятнее ?

                                                                                    Я лично не вижу нигде в этом коде где бы Head'у или NewNode->Next'у выставлялся nullptr. Или по стандарту неинициализированные указатели у нас всегда равны nullptr?

                                                                                    Добавлено
                                                                                    Цитата JoeUser @
                                                                                    Но, по-моему, ты считаешь себя умнее Энтони Уильямса.
                                                                                    При всем моем уважении к тебе - я уверен, ты себя
                                                                                    переоцениваешь.

                                                                                    Речь идет о твоем коде вообще то, а про Энтони Уильямса.
                                                                                    Сообщение отредактировано: Wound -
                                                                                      Цитата JoeUser @
                                                                                      1) Где какие ошибки я допустил?
                                                                                      Как минимум не инициализировал FreeStack::Head.
                                                                                      Цитата JoeUser @
                                                                                      2) В каких местах имеет смысл ловить и обрабатывать исключения?
                                                                                      Не надо. Слишком простой пример. В целом если, конечно любую стартовую функцию потока нужно рассматривать наравне с main(). У тебя это WriterThread::operator()(). Неперехваченное исключение прибьёт всё приложение. Если это не устраивает, то нужно как-то хендлить.
                                                                                      Цитата JoeUser @
                                                                                      3) По скольку отдельная нить не заточена на вычисления, а скорее на ожидания - какой интервал опроса имеет смысл оставить, чтобы попусту не грузить систему?
                                                                                      Ну блин. Кто ж помимо тебя на это ответит-то? Зависит от задачи. Юзеру с головой достаточно 100мс реакции, процессам реального времени и 1мс может оказаться много.
                                                                                        Цитата JoeUser @
                                                                                        а стараюсь оперативно решать текущие вопросы.

                                                                                        Выдать сложный алгоритм вместо простого без малейших обоснований того, что простой будет работать так же эффективно это что угодно, но не "оперативное решение текущих вопросов".
                                                                                          Цитата Pavia @
                                                                                          По поводу QT проблемы с глобальными сиглтонами из-за параллельной сборки он в исполняемые файлы пихает несколько штук. Пришлось через разделяемую память делать свой синглтон и на основе него мьютексы для блокировок.
                                                                                          Цитата Pavia @
                                                                                          Я вам рассказал про подводные камни. А что с ними делать это вам решать.
                                                                                          Этих фраз даже я не понял. Набор слов. Pavia, попрошу быть конкретнее, чтобы тебя можно было понимать по-русски. Так что, увы, не рассказал.
                                                                                          Сообщение отредактировано: Qraizer -
                                                                                            OpenGL, ты мне помнится доказывал что в С++ утечек нет. Узри:
                                                                                            Цитата Wound @
                                                                                            Node<T>* const NewNode = new Node<T>(iData); //! <<< Выделение памяти

                                                                                            И где этот хваленный RAII ? И вот такого кода тонны по всюду.
                                                                                            У него в Попе стэка или в том же пуше, после выделения памяти произойдет исключение - и утечка ресурса гарантирована.
                                                                                              А пока людям, которые думают, что классический подход к многопоточности устарел, предлагаю просветиться вот тут. Очень занятное чтиво. Будет чем пока заняться.
                                                                                              Сообщение отредактировано: Qraizer -
                                                                                                Цитата OpenGL @
                                                                                                Цитата JoeUser @
                                                                                                Мусье, а вы бы мне могли обосновать необходимость блокировки, где и без нее можно обойтись?

                                                                                                Звучит примерно как "обоснуй необходимость компилятора, ведь можно в hex кодах всё написать". Можно, конечно, просто это сложно. lock free алгоритмы в разы более сложные в написании и отладке, да ещё и далеко не всегда они работают быстрее (по-моему очередь и стек так вообще часто медленней оказываются, чем с блокировкой), так что в данном случае всё наоборот - ты должен обосновать необходимость реализовывать с lock-free. У тебя есть замеры времени в реальных условиях, из которых следует, что ты много времени тратишь на на ожидания в мьютексах? Или может быть твоя основная цель это научиться работать с lock-free? Если ответа на оба вопроса "нет", то твоё решение менее рационально, чем решение с блокировками.

                                                                                                Нет. Я тоже, к примеру, не буду оборачивать в смарт какой-нибудь поинтер на что-либо, который всего лишь нужно передать куда-то в legacy API как константу. Если что-то можно сделать просто, это нужно делать просто. В hex набирать программу всегда непросто... хотя бывают гурманы.

                                                                                                Добавлено
                                                                                                Цитата Wound @
                                                                                                OpenGL, ты мне помнится доказывал что в С++ утечек нет. Узри:
                                                                                                Цитата Wound @
                                                                                                Node<T>* const NewNode = new Node<T>(iData); //! <<< Выделение памяти

                                                                                                И где этот хваленный RAII ? И вот такого кода тонны по всюду.
                                                                                                У него в Попе стэка или в том же пуше, после выделения памяти произойдет исключение - и утечка ресурса гарантирована.

                                                                                                Создание объекта new гарантирует отсутствие утечек. Операции с атомиками там бессбойные. Операции с сырым поинтером бессбойные. Проблем не ожидается.
                                                                                                Сообщение отредактировано: Qraizer -
                                                                                                  Цитата Qraizer @
                                                                                                  Создание объекта new гарантирует отсутствие утечек. Операции с атомиками там бессбойные. Операции с сырым поинтером бессбойные. Проблем не ожидается.

                                                                                                  Сбойные, как минимум в Pop методе, в указателе может быть мусор, из за чего полетит AV при вызове - OldHead>Next

                                                                                                  Добавлено
                                                                                                  И когда это new - гарантировал отсуствие утечек? Память он выделил, а дальше в методе вылазиит исключение - и что это гарантирует отсуствие утечек памяти? В С++ GC завезли или я что то пропустил?
                                                                                                    JoeUser, фрилок очень занятная и полезная штука. Но ты должен понимать, что оно лишь обеспечивает целостность тривиальных операций языка, но никак ничего не охраняет от коллизий. Именно поэтому и придуманы были объекты синхронизации, чтобы будучи реализованными на фрилуках где-то глубоко в своём ядре, дать тебе возможность писать защищённый от коллизий код произвольной сложности. Без них ты сам должен это делать. Захватывать объект, для чего использовать спинлук, и в итоге собрать свой собственный объект. Тот самый, использования которого хотел избежать. Вот у тебя и спрашивают, а зачем оно тебе? Непонятно только, почему таким заумным способом спрашивают. Очень редко бывает, когда достаточно просто атомарно сделать что-то тривиальное. Ну вот у тебя это манипуляция Next, например. Ну ок. А теперь у тебя двусвязный список. А у него два поинтера. Ну-ка представь себе атомарное изменение двух поинтеров. Как ты себе это представишь иначе, кроме как булевый флаг, который спинится на "занят"/"свободен" и под "занят" менять, причём уже без всяких атомиков (хотя и с барьерами). И чем тебе этот флаг не мьютекс тогда? Представь теперь что тебе не надо менять, а надо просто пройти по этому списку. Без подобного булевого флага обойдёшься? Точно? Уверен?
                                                                                                    Использование принципов фрилок сродни "я сам всё знаю и за всем слежу". Есть любители нативных поинтеров или там ручного управления ресурсами. В ряде случаев оно даже бывает оправдано. Главное тут – чуять грань. Попробуй вместо одной нитки у себя в примере завести несколько. Почувствуй т.с. руками, каково это.

                                                                                                    Добавлено
                                                                                                    Цитата Wound @
                                                                                                    И когда это new - гарантировал отсуствие утечек? Память он выделил, а дальше в методе вылазиит исключение - и что это гарантирует отсуствие утечек памяти?
                                                                                                    Да. Если new или даже new[], успешно распределили память, но в конструкторе одного из объектов произошёл бадабум, то все уже сконструированные объекты, как и положено, удаляются и выделенная new или new[] память освобождается.
                                                                                                      Цитата Qraizer @
                                                                                                      Я тоже, к примеру, не буду оборачивать в смарт какой-нибудь поинтер на что-либо, который всего лишь нужно передать куда-то в legacy API как константу.

                                                                                                      Я не понял, к чему этот пример. Речь о том, что ТС предпочёл сложное решение простому при отсутствии хоть каких-либо предпосылок к тому, что сложное окажется эффективнее. Это примерно как писать сортировку вставками, не произведя замеры и не убедившись, что std::sort в данном случае окажется хуже.
                                                                                                        Цитата Qraizer @
                                                                                                        Да. Если new или даже new[], успешно распределили память, но в конструкторе одного из объектов произошёл бадабум, то все уже сконструированные объекты, как и положено, удаляются и выделенная new или new[] память освобождается.

                                                                                                        Вообще-то в его коде другая ситуация. Не та о которой говоришь ты.
                                                                                                          Цитата OpenGL @
                                                                                                          Речь о том, что ТС предпочёл сложное решение простому при отсутствии хоть каких-либо предпосылок к тому, что сложное окажется эффективнее. Это примерно как писать сортировку вставками, не произведя замеры и не убедившись, что std::sort в данном случае окажется хуже.
                                                                                                          Это его дело. Давайте не будем, как любят цитировать по поводу русских форумов, отвечать человеку на не заданные вопросы. Мне понадобился всего абзац чётких фактов вместо трёх страниц вокруг да около, дальше пусть решает сам. И кстати, задача у него действительно пока проста – управление одной переменной.
                                                                                                            Цитата Qraizer @
                                                                                                            Это его дело.

                                                                                                            Я с этим и не спорил изначально. Спросил только, почему не взял/скопипастил известную lock-free библиотеку (мало ли, вдруг не знает её), а с ТЗ и выбранным способом решения пусть сам разбирается. Кто я, в конце-концов, такой, чтобы мешать усложнить ТС себе жизнь? :D Но вот это:
                                                                                                            Цитата JoeUser @
                                                                                                            Вот пошла тема!!! Рациональность. Прекрасненько. Тут есть о чем поговорить. Расскажи мне, пожалуйста, как сделать рациональнее!

                                                                                                            по-моему явно сказало о том, что ТС и о разумности подхода поспорить хочет. Так что всё произошедшее тут это исключительно следствие неумения читать чужие аргументы.
                                                                                                              Цитата Qraizer @
                                                                                                              Как минимум не инициализировал FreeStack::Head.

                                                                                                              Вот. То что я просил. Согласен и исправлю. Но пока не понял почему эта переменная инициализируется нулями. За все попытки запуска ни разу не сбойнуло. Странно. :-?

                                                                                                              Цитата ЫукпШ @
                                                                                                              Вставил 2 карт-ридера и скопировал с одной карты на другую.

                                                                                                              Ну это явно не мой вариант! У меня читаются только карточки с магнитной полосой, вмещающие сотни байт, даже не килобайты. Это примерно тоже самое, если к компу подключить вторую клавиатуру. Можно. Только бессмысленно.

                                                                                                              Цитата Qraizer @
                                                                                                              Неперехваченное исключение прибьёт всё приложение. Если это не устраивает, то нужно как-то хендлить.

                                                                                                              Скорее всего так и есть. По идее любой new нужно оборачивать в try-catch (а вдруг памяти не хватит), но на практике мало кто это делает. В моем реальном (не синтетическом) случае тож скорее всего исключений не будет, я не вижу где.

                                                                                                              Цитата Qraizer @
                                                                                                              Юзеру с головой достаточно 100мс реакции

                                                                                                              Я примерно тоже к такой цифре склоняюсь, хотя буду пользовать скорее всего 50мс. Посмотрел по диспетчеру задач - загрузка CPU прогой при 100 и 50 практически равная - доли процента.
                                                                                                                Цитата OpenGL @
                                                                                                                неумения читать чужие аргументы

                                                                                                                Или не умение их писать?
                                                                                                                Вон пришел Qraizer, без лишних словоблудий нашел и показал косяк (с инициализацией).
                                                                                                                Вот такое я читать умею и люблю.
                                                                                                                  Цитата Qraizer @
                                                                                                                  А теперь у тебя двусвязный список.

                                                                                                                  Это я понимаю! Есть структуры (мой случай), когда free-lock реализуется просто и на ура. А есть структуры, тот же двусвязный список, где это реализовать невозможно или крайне затруднительно. В моем вопросе - мне просто повезло.
                                                                                                                    Цитата OpenGL @
                                                                                                                    Спросил только, почему не взял/скопипастил известную lock-free библиотеку (мало ли, вдруг не знает её)

                                                                                                                    А нафига? У меня же тривиальнейший случай.

                                                                                                                    Кстати, на счет советов с мьютексами для моего случая - я это воспринимаю примерно так:
                                                                                                                    ExpandedWrap disabled
                                                                                                                      if (а == 1) {
                                                                                                                        // на всякий случай, чтобы уж точно
                                                                                                                        if (а == 1) {
                                                                                                                          b=1;
                                                                                                                          break;
                                                                                                                        }
                                                                                                                      }

                                                                                                                    Можно и так. Только зачем.

                                                                                                                    Добавлено
                                                                                                                    Цитата Wound @
                                                                                                                    И где этот хваленный RAII ?

                                                                                                                    Хваленого RAII нет, но за место возможной утечки - сенкс. Действительно, часть кода не моя,
                                                                                                                    с книжки. И у меня тоже возник вопрос - new есть, а delete нету. Разве только что это не
                                                                                                                    обеспечивается std::shared_ptr :-?
                                                                                                                      А на что тогда, по-твоему, std::shared_ptr<> нужен? Накой кому нужен просто абстрактный счётчик копий?

                                                                                                                      Добавлено
                                                                                                                      Цитата JoeUser @
                                                                                                                      Вот. То что я просил. Согласен и исправлю. Но пока не понял почему эта переменная инициализируется нулями. За все попытки запуска ни разу не сбойнуло. Странно.
                                                                                                                      Ну, тебе на это ещё Wound указал до меня. А так-то атомики без явной инициализации инитятся дефолтом, т.е. никак.
                                                                                                                      Сообщение отредактировано: Qraizer -
                                                                                                                        Цитата JoeUser @
                                                                                                                        Вон пришел Qraizer, без лишних словоблудий нашел и показал косяк (с инициализацией).
                                                                                                                        Вот такое я читать умею и люблю.

                                                                                                                        Угу, конечно, я тебе на этот косяк указал еще раньше. Но ты почему то начал обвинять меня в том, что я строю из себя шибко умного. :-?
                                                                                                                          ExpandedWrap disabled
                                                                                                                            std::shared_ptr<T> Data;
                                                                                                                             
                                                                                                                            Node(T const& iData): Data(std::make_shared<T>(iData)) {  }
                                                                                                                             
                                                                                                                            Node<T>* const NewNode = new Node<T>(iData);
                                                                                                                          new выделяет память и конструирует на ней iData. Если что-то пойдёт не так, память освободится. Что может пойти не так? Конструктор Node<T> делает make_shared, который потенциально выделяет память, т.к. конструирует shared_ptr<>. Если что-то пойдёт не так, в частности из-за конструктора копии T aka iData, это проблемы make_shared(), чтобы не допустить утечек. По-любому это будет конструктор Node<T>, так что мимо внимания new сие не пройдёт. Конструктор копии shared_ptr имеет атрибут nothrow, и тем более его имеет перемещающий конструктор, который будет задействован в твоём случае. Что не удивительно ни разу, но убедиться было не лишним.
                                                                                                                          При неуспешном создании NewNode все проблемы локализованы внутри new. После успешного создания NewNode остальные операции nothrow. Откуда утечки?

                                                                                                                          Добавлено
                                                                                                                          Ещё мне не нравится, что твой Pop() может вернуть ошибку посредством пустого shared_ptr. Придётся везде проверки пихать. Но с фрилуком иначе, боюсь, и не получится.

                                                                                                                          Добавлено
                                                                                                                          P.S.
                                                                                                                          Цитата Qraizer @
                                                                                                                          Откуда утечки?
                                                                                                                          Если там намёк на отсутствие деструктора у стека, то это уже другой вопрос.
                                                                                                                          Сообщение отредактировано: Qraizer -
                                                                                                                            Wound, Qraizer, косяк с утечкой всеж был.
                                                                                                                            И Киля, спасибо за наводку!

                                                                                                                            Я зачекал, изменил вот так:
                                                                                                                            ExpandedWrap disabled
                                                                                                                              template <typename T>
                                                                                                                              struct Node {
                                                                                                                                  std::shared_ptr<T> Data;
                                                                                                                                  Node* Next;
                                                                                                                                  Node(T const& iData):   Data(std::make_shared<T>(iData)) {
                                                                                                                                      std::cout << "Node start" << std:: endl;
                                                                                                                                  }
                                                                                                                                  ~Node() {
                                                                                                                                      std::cout << "Node stop" << std:: endl;
                                                                                                                                  }
                                                                                                                              };

                                                                                                                            Освобождений не было. Изменил код:

                                                                                                                            ExpandedWrap disabled
                                                                                                                              template <typename T>
                                                                                                                              class FreeStack {
                                                                                                                                      std::atomic<Node<T>*> Head;
                                                                                                                                  public:
                                                                                                                                      void Push(T const& iData) {
                                                                                                                                          Node<T>* const NewNode = new Node<T>(iData);
                                                                                                                                          NewNode->Next = Head.load();
                                                                                                                                          while (!Head.compare_exchange_weak(NewNode->Next, NewNode));
                                                                                                                                      }
                                                                                                                                      std::shared_ptr<T> Pop() {
                                                                                                                                          Node<T>* OldHead = Head.load();
                                                                                                                                          while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next));
                                                                                                                                          if (OldHead) {
                                                                                                                                              auto Ret = OldHead->Data;
                                                                                                                                              delete OldHead; // <---------------------- тут
                                                                                                                                              return Ret;
                                                                                                                                          } else
                                                                                                                                              return std::shared_ptr<T>();
                                                                                                                                      }
                                                                                                                              };

                                                                                                                            А вот теперь все ровно! Что выделяется, то потом и освобождается.

                                                                                                                            Добавлено
                                                                                                                            Хотя, странно :-? push() и pop() я взял из книжки чисто почти копипастой.
                                                                                                                            Неужели не проверяют, что пишут ...

                                                                                                                            Добавлено
                                                                                                                            И кстати, да - вопрос к автору книжки, этому Уильямсу ... Если есть такой
                                                                                                                            жесткий косяк с утечкой памяти, то что остального полезного можно получить
                                                                                                                            из раздела о lock-free структурах, кроме как упоминания полезного метода
                                                                                                                            std::atomic::compare_exchange_weak?

                                                                                                                            :-?
                                                                                                                              И нахрена тогда std::shared_ptr раз память все равно течет? Вопросов больше чем ответов.
                                                                                                                              Может из стека снимать ноду нужно как-то иначе? А-ля в стиле std::move?
                                                                                                                                Скрытый текст
                                                                                                                                Цитата JoeUser @
                                                                                                                                А нафига? У меня же тривиальнейший случай.

                                                                                                                                Собственно, чтд - ты просто не читаешь, что тебе пишут :yes: "Нафига" и я, и Киля, и Qraizer объясняли уже несколько раз.
                                                                                                                                Да и "тривиальнейший случай", который разбирается тут уже пять страниц (три, если вычесть срач) это отдельный прикол. Сам-то не видишь тут противоречия? :jokingly:


                                                                                                                                Цитата JoeUser @
                                                                                                                                Может из стека снимать ноду нужно как-то иначе? А-ля в стиле std::move?

                                                                                                                                Нет. Все эти compare_exchange и прочие атомарные операции в силу своей природы работают только с выровненными данными, и размер этих данных у тебя не будет больше 8 байт, так что мувить атомарно у тебя получится только указатели. Возможно, это означает, что его можно заставить работать с каким-нибудь unique_ptr (без кастомного deleter-а, разумеется), но на практике я не слышал, чтобы так делали, поэтому просто работают с голыми указателями и обеспечивают невозможность их утечки исключительно внимательной медитацией над кодом.
                                                                                                                                  Цитата JoeUser @
                                                                                                                                  Освобождений не было. Изменил код:

                                                                                                                                  Я если честно так и не увидел - где ты изменил код? У тебя как было AV, так оно и осталось ровно в том же месте.
                                                                                                                                  Если первый поток успел вызвать Pop раньше, чем второй Push то во второй строке будет AV:
                                                                                                                                  ExpandedWrap disabled
                                                                                                                                    Node<T>* OldHead = Head.load();
                                                                                                                                                while (OldHead && !Head.compare_exchange_weak(OldHead, OldHead->Next)); //! <<<<< Тут бьудет Access Violation, конкретно на вот этой конструкции - OldHead->Next

                                                                                                                                  Пропустил или забыл что ли?

                                                                                                                                  На всякий случай просто приведу пример.
                                                                                                                                  Ради понимания, попробуй создать консольное приложение и набери там, ну либо просто внимательно взгляни на этот код:

                                                                                                                                  ExpandedWrap disabled
                                                                                                                                        template <typename T>
                                                                                                                                        struct Node {
                                                                                                                                            std::shared_ptr<T> Data;
                                                                                                                                            Node* Next;
                                                                                                                                            Node(T const& iData):   Data(std::make_shared<T>(iData)) {
                                                                                                                                                std::cout << "Node start" << std:: endl;
                                                                                                                                            }
                                                                                                                                            ~Node() {
                                                                                                                                                std::cout << "Node stop" << std:: endl;
                                                                                                                                            }
                                                                                                                                        };
                                                                                                                                     
                                                                                                                                    int main()
                                                                                                                                    {
                                                                                                                                         std::atomic<Node<T>*> Head;
                                                                                                                                         Node<T>* OldHead = Head.load();
                                                                                                                                     
                                                                                                                                         std::cout << OldHead->Next->Data; //! <<< Вот тут ошибка, и у тебя ровно такая же.
                                                                                                                                    }
                                                                                                                                    Цитата OpenGL @
                                                                                                                                    Сам-то не видишь тут противоречия?

                                                                                                                                    Противоречие одно - эти механизмы нечасто были использованы. А ведь кода совсем мало.
                                                                                                                                    Поэтому и столько обсуждений. А не потому, что тут что-то из ряда вон громоздкое и
                                                                                                                                    сложное. ИМХО.
                                                                                                                                      А вообще по-моему у тебя тут есть риск вытащить один и тот же элемент дважды, если звёзды не сошлись, из-за aba проблемы. Да, вероятность мала, но тем не менее
                                                                                                                                        Цитата Qraizer @
                                                                                                                                        Цитата Pavia @
                                                                                                                                        По поводу QT проблемы с глобальными сиглтонами из-за параллельной сборки он в исполняемые файлы пихает несколько штук. Пришлось через разделяемую память делать свой синглтон и на основе него мьютексы для блокировок.
                                                                                                                                        Цитата Pavia @
                                                                                                                                        Я вам рассказал про подводные камни. А что с ними делать это вам решать.
                                                                                                                                        Этих фраз даже я не понял. Набор слов. Pavia, попрошу быть конкретнее, чтобы тебя можно было понимать по-русски. Так что, увы, не рассказал.

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

                                                                                                                                        Тест я год назад делал вот выложен:
                                                                                                                                        https://www.programmersforum.ru/showthread.php?t=328066

                                                                                                                                        Сейчас я в командировке и сделать скриншоты с бинарником и отладчиком не получится... Судя по коду это вариант с 1 Юнитом. А так для полной демонстрации нужно сделать 2-х агентов для наглядности.

                                                                                                                                        PS. Постараюсь на следующей неделе сделать.
                                                                                                                                        Сообщение отредактировано: Pavia -
                                                                                                                                          Цитата Wound @
                                                                                                                                          Ради понимания, попробуй создать консольное приложение и набери там, ну либо просто внимательно взгляни на этот код:

                                                                                                                                          Ты привел дичь, которая не компилируется.

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

                                                                                                                                          ExpandedWrap disabled
                                                                                                                                            #include <iostream>
                                                                                                                                            #include <memory>
                                                                                                                                            #include <atomic>
                                                                                                                                             
                                                                                                                                            using namespace std;
                                                                                                                                             
                                                                                                                                            template <typename T>
                                                                                                                                            struct Node {
                                                                                                                                                std::shared_ptr<T> Data;
                                                                                                                                                Node* Next;
                                                                                                                                                Node(T const& iData):   Data(std::make_shared<T>(iData)) {
                                                                                                                                                    std::cout << "Node start" << std:: endl;
                                                                                                                                                }
                                                                                                                                                ~Node() {
                                                                                                                                                    std::cout << "Node stop" << std:: endl;
                                                                                                                                                }
                                                                                                                                            };
                                                                                                                                             
                                                                                                                                            int main() {
                                                                                                                                              std::atomic<Node<std::string>*> Head;
                                                                                                                                              Node<std::string>* OldHead = Head.load();
                                                                                                                                              if (OldHead) std::cout << OldHead->Next;  
                                                                                                                                            }

                                                                                                                                          Все работает.
                                                                                                                                          А в моем коде есть строчка:

                                                                                                                                          ExpandedWrap disabled
                                                                                                                                            return OldHead ? OldHead->Data : std::shared_ptr<T>();

                                                                                                                                          Т.е., как видишь, указатель сперва валидируется, и только если норм берется его значение поля OldHead->Data.
                                                                                                                                          А это ты почему-то проигнорил :-?

                                                                                                                                          Хотя, все равно, код не одинаковый. Мою прогу сколько раз не запускал - сбоя ни разу (вообще ни разу) не было.
                                                                                                                                          Буду разбираться, где же и почему "мне повезло".

                                                                                                                                          Добавлено
                                                                                                                                          Цитата OpenGL @
                                                                                                                                          если звёзды не сошлись, из-за aba проблемы

                                                                                                                                          Можно подробнее, а что это за "aba проблема"?

                                                                                                                                          Добавлено
                                                                                                                                          Немного исправил код Run()
                                                                                                                                          ExpandedWrap disabled
                                                                                                                                             void Run() {
                                                                                                                                              Node<std::string>* Tmp = Stack.Head.load();
                                                                                                                                              std::cout << Tmp << std::endl;

                                                                                                                                          И прогнал под отладкой - Tmp получает nullptr
                                                                                                                                          А как я уже писал выше, у меня Pop() работает с проверкой
                                                                                                                                          ExpandedWrap disabled
                                                                                                                                             return OldHead ? OldHead->Data : std::shared_ptr<T>();

                                                                                                                                          Так что программа не валится. Но "да", с явной инициализацией вершины будет точнее.

                                                                                                                                          Добавлено
                                                                                                                                          Цитата Qraizer @
                                                                                                                                          Ещё мне не нравится, что твой Pop() может вернуть ошибку посредством пустого shared_ptr. Придётся везде проверки пихать. Но с фрилуком иначе, боюсь, и не получится.

                                                                                                                                          Вечером попробую тест поставить - вообще ничего не ложить в стек, а в цикле пробовать все время брать из пустого. Ну и запущу под Линухом под Valgrind'ом.
                                                                                                                                            Цитата Wound @
                                                                                                                                            Если первый поток успел вызвать Pop раньше, чем второй Push то во второй строке будет AV:

                                                                                                                                            Похоже что оч редко (именно в момент переключения потоков), но потенциально может.
                                                                                                                                            Буду это изучать - по идее должно как-то помочь в данной ситуации :whistle:
                                                                                                                                              Простая задача, он говорил :lol: А ведь ты ещё даже не учитываешь барьеры, не слышал про ABA (которая, кстати, может и не проявится - хз, думать надо) и вообще у тебя пока ещё один читатель и один писатель, что жизнь сильно упрощает :lool:
                                                                                                                                                OpenGL, прочёл про ABA - это ясно не мой случай. В этой проблеме предполагается возможность прерывания и продолжения операций. У меня же операции атомарные. И я манипулирую адресами, а одинаковое значение указателя не может ссылаться на разные ячейки памяти.
                                                                                                                                                  Цитата JoeUser @
                                                                                                                                                  В этой проблеме предполагается возможность прерывания и продолжения операций.

                                                                                                                                                  Нет, не предполагается. ABA это про ситуацию, когда менеджер памяти выдал указатель на ячейку памяти, которая юзалась ранее, но уже была освобождена. Тогда у тебя появляется шанс на успешный compare_exchange в то время, когда он не должен быть таковым.
                                                                                                                                                  И кстати, я только сейчас заметил, что добавление второго читателя или писателя у тебя всё сломает даже безотносительно ABA.
                                                                                                                                                    Цитата JoeUser @
                                                                                                                                                    И нахрена тогда std::shared_ptr раз память все равно течет? Вопросов больше чем ответов.
                                                                                                                                                    Потому что у тебя не shared_ptr, а указатель на shared_ptr. Точнее, на Node, внутри которого shared_ptr, неважно.

                                                                                                                                                    Добавлено
                                                                                                                                                    Цитата Pavia @
                                                                                                                                                    Это отдельная тема для разговора. Одно описание проблемы займет несколько листов.
                                                                                                                                                    Зачем оно нам? Тебе было сложно написать два предложения по-русски, а не тарабарски?
                                                                                                                                                      Цитата JoeUser @
                                                                                                                                                      Ты привел дичь, которая не компилируется.

                                                                                                                                                      Это не дичь, а псевдокод, чтоб показать проблему.

                                                                                                                                                      Цитата JoeUser @
                                                                                                                                                      Все работает.
                                                                                                                                                      А в моем коде есть строчка:

                                                                                                                                                      И судя по всему ты не увидел в этом коде проблемы. И это пичально.
                                                                                                                                                      Не знаю как тебе еще пояснить, тебе и Qraizer и я писал. Но ты не увидел проблемы. Ты вообще в дебаге наверно запускал, вот у тебя и работало все, или так звезды сошлись, что неинициализированный указатель у тебя оказался 0. Короче ладно, пофигу. Когда AV выхватишь в проде, тогда до тебя дойдет в чем у тебя ошибка заключается, а ты ее выхватишь в любом случае рано или поздно.

                                                                                                                                                      Цитата JoeUser @
                                                                                                                                                      Т.е., как видишь, указатель сперва валидируется, и только если норм берется его значение поля OldHead->Data.
                                                                                                                                                      А это ты почему-то проигнорил

                                                                                                                                                      На что валидируется? На то что если он не нулевой, то вызвать член через указатель? А расскажи ка, когда он у тебя будет нулевым? В каком месте он у тебя нулевой? Там мусор будет содержатся, и соответственно твоя валидация сработает не так, как ты ожидаешь.

                                                                                                                                                      Цитата JoeUser @
                                                                                                                                                      Так что программа не валится. Но "да", с явной инициализацией вершины будет точнее.

                                                                                                                                                      То что она у тебя не валиться еще ни о чем совершенно не говорит.
                                                                                                                                                        Цитата Wound @
                                                                                                                                                        Ты вообще в дебаге наверно запускал, вот у тебя и работало все, или так звезды сошлись, что неинициализированный указатель у тебя оказался 0.

                                                                                                                                                        Скорее всего так сошлись звезды :lol: Но это полный песец, и объяснения у меня этому нет.
                                                                                                                                                        Да, я с тобой сейчас согласен. Да и ранее, в принципе, с твоей наводки увидел не инициализируемое
                                                                                                                                                        поле класса. Но ведь работало - и это меня смущало, что программа должна падать, а она никак и не
                                                                                                                                                        падает! И вот утром пришла мысль поэксперементировать с режимами сборки, и вот какая любопытная
                                                                                                                                                        табличка у меня вышла:

                                                                                                                                                        Сборкас++11с++14с++17
                                                                                                                                                        MSVS 2017 Debug x32--+
                                                                                                                                                        MSVS 2017 Release x32---
                                                                                                                                                        MinGW 7.3.0 Debug x32---
                                                                                                                                                        MinGW 7.3.0 Release x32---

                                                                                                                                                        где вылет '-', а норм работа '+'

                                                                                                                                                        А у меня как раз все сборки и тесты были под MSVS 2017 Debug x32 с++17

                                                                                                                                                        Да, сейчас изменил - добавил инициализацию nullptr

                                                                                                                                                        ExpandedWrap disabled
                                                                                                                                                          template <typename T>
                                                                                                                                                          class FreeStack {
                                                                                                                                                              std::atomic<Node<T>*> Head = nullptr; // <---------------- вот тут
                                                                                                                                                            public:

                                                                                                                                                        И во всех вариантах работа стала нормальной. Получается с MSVS 2017 Debug x32 с++17
                                                                                                                                                        какая-то мистика.
                                                                                                                                                          Цитата Wound @
                                                                                                                                                          На что валидируется? На то что если он не нулевой, то вызвать член через указатель? А расскажи ка, когда он у тебя будет нулевым? В каком месте он у тебя нулевой? Там мусор будет содержатся, и соответственно твоя валидация сработает не так, как ты ожидаешь.

                                                                                                                                                          Ты прав.

                                                                                                                                                          Цитата Wound @
                                                                                                                                                          То что она у тебя не валиться еще ни о чем совершенно не говорит.

                                                                                                                                                          Ты прав.

                                                                                                                                                          :blush:
                                                                                                                                                            В дебаге там сразу 0 на стеке, в релизе не 0.
                                                                                                                                                              Цитата Qraizer @
                                                                                                                                                              В дебаге там сразу 0 на стеке

                                                                                                                                                              Ну это я уж догадался. Вопрос в другом. Почему в остальных (с++11, с++14) в дебаге
                                                                                                                                                              не нуль, а мне только"повезло" с с++17 и только со студийным компилером?
                                                                                                                                                              Вот тут мистика.
                                                                                                                                                                Ну мож в новых студиях стали очищать стэк. Заботятся. :rolleyes:
                                                                                                                                                                  Цитата JoeUser @
                                                                                                                                                                  Почему в остальных (с++11, с++14) в дебаге
                                                                                                                                                                  не нуль
                                                                                                                                                                  Потому что стандарт не требует инициировать переменные на стеке нулём.
                                                                                                                                                                    Та не, просто повезло. Хотя в данном случае, скорее не повезло. 100пудово в прологе main() стоит что-то типа sub esp, XX, где XX суммарный размер локальных переменных, и нули весьма глубоко внутри стека на входе в main() никак от самой main() не зависят.
                                                                                                                                                                      Вообще достаточно странно, потому что студия на моей памяти в дебаге всегда переменные инициализировала чем-то вроде 0xCCCCCCCC.
                                                                                                                                                                        Цитата Qraizer @
                                                                                                                                                                        Вообще достаточно странно

                                                                                                                                                                        Да нет, я уже спрашивал. Но мне в тупую ответили:

                                                                                                                                                                        Цитата amk @
                                                                                                                                                                        Потому что стандарт не требует инициировать переменные на стеке нулём.

                                                                                                                                                                        amk, извини, но это СТАНДАРТНЫЙ ОТВЕТ от Копетана Очевидность!

                                                                                                                                                                        Ну не требует и не требует... Но почему одна и та же студия при указании разных стандартов ведет себя по разному?
                                                                                                                                                                        Я же привел таблицу выше!
                                                                                                                                                                          Цитата JoeUser @
                                                                                                                                                                          Но почему одна и та же студия при указании разных стандартов ведет себя по разному?

                                                                                                                                                                          Потому что создатель компилятора поспорил со своим коллегой, что это изменение кодревью пройдёт :D Чисто теоретически ты какой ответ ожидаешь услышать? Такая инициализация это деталь реализации, и она может быть любой, не противоречащей стандарту, что мы и наблюдаем.
                                                                                                                                                                            Цитата JoeUser @
                                                                                                                                                                            Ну не требует и не требует... Но почему одна и та же студия при указании разных стандартов ведет себя по разному?
                                                                                                                                                                            Поиграйся оптимизацией, скорее всего и в дебаге, и в релизе сможешь получить оба эффекта.
                                                                                                                                                                            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                                                                                                                            0 пользователей:


                                                                                                                                                                            Рейтинг@Mail.ru
                                                                                                                                                                            [ Script execution time: 0,2582 ]   [ 17 queries used ]   [ Generated: 16.04.24, 19:30 GMT ]