Хобрук: Ваш путь к мастерству в программировании

Когда межпроцессный процесс библиотеки Boost определяет named_mutex, эти named_mutex правильно работают между разными процессами или только с потоками?

Я думаю, что я должен предполагать что-то из имени boost::interprocess, что не соответствует действительности. В документах повторяется, что named_mutex является глобальным здесь.

Я не могу заставить его работать, хотя. Две копии одного и того же исполняемого файла должны запускаться одновременно, и я ожидаю, что именованный мьютекс в библиотеке с именем boost::interprocess иногда может фактически БЛОКИРОВАТЬСЯ. Это не так. Это также не предотвращает повреждение файла данных в приведенном ниже коде.

Вот некоторый код из документации по повышению:

#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>


int main ()
{
   using namespace boost::interprocess;
   try{
      struct file_remove
      {
         file_remove() { std::remove("file_name"); }
         ~file_remove(){ std::remove("file_name"); }
      } file_remover;
      struct mutex_remove
      {
         mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
         ~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
      } remover;

      //Open or create the named mutex
      named_mutex mutex(open_or_create, "fstream_named_mutex");

      std::ofstream file("file_name");

      for(int i = 0; i < 10; ++i){

         //Do some operations...

         //Write to file atomically
         scoped_lock<named_mutex> lock(mutex);
         file << "Process name, ";
         file << "This is iteration #" << i;
         file << std::endl;
      }
   }
   catch(interprocess_exception &ex){
      std::cout << ex.what() << std::endl;
      return 1;
   }
   return 0;

Вот что я сделал с ним, чтобы доказать себе, что мьютекс что-то делает:

#include <windows.h>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdio>


int main (int argc, char *argv[])
{
   srand((unsigned) time(NULL));

   using namespace boost::interprocess;
   try{
       /*
      struct file_remove
      {
         file_remove() { std::remove("file_name"); }
         ~file_remove(){ std::remove("file_name"); }
      } file_remover;
      */

      struct mutex_remove
      {
         mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
         ~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
      } remover;

      //Open or create the named mutex
      named_mutex mutex(open_or_create, "fstream_named_mutex");

      std::ofstream file("file_name");

      for(int i = 0; i < 100; ++i){

         //Do some operations...

         //Write to file atomically
         DWORD n1,n2;
         n1 = GetTickCount();
         scoped_lock<named_mutex> lock(mutex);
         n2 = GetTickCount();
         std::cout << "took " << (n2-n1) << " msec to acquire mutex";
         int randomtime = rand()%10;
         if (randomtime<1) 
            randomtime = 1;
         Sleep(randomtime*100);
         std::cout << " ... writing...\n";
         if (argc>1)
            file << argv[1];
         else
             file << "SOMETHING";
         file << " This is iteration #" << i;
         file << std::endl;
         file.flush(); // added in case this explains the corruption, it does not.
      }
   }
   catch(interprocess_exception &ex){
      std::cout << "ERROR " << ex.what() << std::endl;
      return 1;
   }
   return 0;
}

Выход консоли:

took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...

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

Я ожидаю, что если я удалю file_name и запущу две копии программы, я должен получить чередующиеся записи в file_name, содержащие 100 строк из каждого экземпляра.

(Обратите внимание, что демонстрационный код явно не использует ofstream в режиме добавления, вместо этого он просто перезаписывает файл каждый раз, когда эта программа запускается, поэтому, если мы хотим, чтобы демонстрация показывала два процесса записи в файл, я знаю об этом Причина, по которой это не сработает, но я ожидал, что приведенный выше код будет возможной демонстрацией взаимного исключения, что не так.Также можно было бы включить вызовы очень удобного и точно названного метода ofstream::flush(), и не было 'т.)

Использование Boost 1.53 в Visual C++ 2008


Ответы:


1

Оказывается, Boost — замечательная библиотека, и ее примеры кода, вкрапленные в документацию, иногда могут быть сломаны. По крайней мере, тот, что указан в документации для boost::interprocess::named_mutex, не работает в системах Windows.

*Всегда удаление мьютекса как части демонстрационного кода приводит к тому, что мьютекс не работает. *

Это должно быть прокомментировано как минимум в демо-коде. Он не соответствует «принципу наименьшего изумления», хотя мне было интересно, почему он там, я думал, что это должно быть идиоматично и необходимо, это идиотизм и ненужность, на самом деле. Или, если необходимо, это пример того, что Джоэл Спольски назвал бы дырявой абстракцией. Если мьютексы действительно являются точками файловой системы под C:\ProgramData в Windows, я уверен, что не хочу знать об этом или знать, что дерьмо останется позади, что нарушит абстракцию, если я не обнаружу этот случай и не уберу его. (Конечно, запах дружественной к posix семантики для мьютексов в Boost заставил их использовать реализацию в стиле posix вместо того, чтобы напрямую обращаться к Win32 API и реализовывать простой мьютекс, у которого нет дерьма файловой системы.)

Вот рабочая демонстрация:

#include <windows.h>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>

#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>
#include <windows.h>

int main (int argc, char *argv[])
{
   srand((unsigned) time(NULL));

   using namespace boost::interprocess;
   try{
       /*
      // UNCOMMENT THIS IF YOU WANT TO MAKE THIS DEMO IMPOSSIBLE TO USE TO DEMO ANYTHING

      struct file_remove
      {
         file_remove() { std::remove("file_name"); }
         ~file_remove(){ std::remove("file_name"); }
      } file_remover;

      // UNCOMMENT THIS IF YOU WANT TO BREAK THIS DEMO HORRIBLY:

      struct mutex_remove
      {
         mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
         ~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
      } remover;
      */

      //Open or create the named mutex
      named_mutex mutex(open_or_create, "fstream_named_mutex");

      std::ofstream file("file_name", std::ios_base::app );
      int randomtime = 0;
      for(int i = 0; i < 100; ++i){

         //Do some operations...

         //Write to file atomically
         DWORD n1,n2;
         n1 = GetTickCount();

         {
         scoped_lock<named_mutex> lock(mutex);
         n2 = GetTickCount();
         std::cout << "took " << (n2-n1) << " msec to acquire mutex";

         randomtime = rand()%10;
         if (randomtime<1) 
            randomtime = 1;

         std::cout << " ... writing...\n";
         if (argc>1)
            file << argv[1];
         else
             file << "SOMETHING";
         file << "...";
         Sleep(randomtime*100);
         file << " This is iteration #" << i;
         file << std::endl;
         file.flush();
         }
         Sleep(randomtime*100); // let the other guy in.
      }
   }
   catch(interprocess_exception &ex){
      std::cout << "ERROR " << ex.what() << std::endl;
      return 1;
   }
   return 0;
}

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

Чтобы использовать демо:
- Соберите его и запустите две его копии. Передайте параметр, чтобы вы могли видеть, какой экземпляр написал какие строки (start myexename ABC и start myexename DEF из командной строки в Windows). первый.

введите здесь описание изображения

09.03.2013
  • Я понятия не имею, что это делает (если вообще что-то делает), но в исходном примере вам не хватало (void)remover; перед named_mutex mutex(.... 09.03.2013
  • Я считаю, что идея с remover состоит в том, чтобы удалить любой возможный мьютекс, который может остаться от предыдущего выполнения, перед созданием нового мьютекса, а затем удалить его в конце, как только remover выйдет за рамки. Я действительно понятия не имею, что делает (void)remover;, но, возможно, это способ заставить компилятор не оптимизировать remover. И то же самое с file_remover. 09.03.2013
  • Ну, удаление мьютекса каждый раз, когда он запускается, полностью ПОРАЖАЕТ цель самих мьютексов, и поэтому эта демонстрация является анти-демонстрацией ужасной наихудшей практики, либо это, либо основы этого в Windows ужасны. 09.03.2013
  • Вы должны удалить мьютекс один раз перед запуском демонстрации, а не в каждом экземпляре демонстрации. 09.03.2013
  • Тенденция, которую имеют новые пользователи, состоит в том, чтобы просмотреть демонстрацию, поскольку это то, как вы это делаете. Поскольку, по-видимому, при обычном использовании вам не нужно их удалять, этого не должно быть в демоверсии. На самом деле, обычно Boost очищает себя после себя, и это заставляет меня задуматься, могу ли я доверять этой библиотеке, если мне иногда приходится исправлять эти мьютексы таким образом, чтобы они действительно ломались. 09.03.2013
  • Документы ясно указывают на то, что система «постоянство в файлах» не удаляется, но удаление файлов разрушает взаимное исключение. 12.03.2013
  • Итак.... When boost library “interprocess” defines a named_mutex do those named_mutexes work properly between different processes, or only with threads? Вы прямо не отвечаете на вопрос - но исправляете код 24.06.2020
  • Новые материалы

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

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

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..