【问题标题】:Creating global named counter shared between processes创建进程之间共享的全局命名计数器
【发布时间】:2011-05-14 16:26:07
【问题描述】:

如何在 c++ 中创建一个可以在多个进程之间共享的全局计数器值?我需要的是一种同时“使”多个进程“无效”的方法,向它们发出信号以执行某些操作(例如从文件中读取)。所有进程将连续轮询(每 10 毫秒)以获取当前计数器值,并将其与内部存储的最后一个值进行比较。不匹配的值表明需要做一些工作。

编辑:顺便说一句,我的进程作为不同的 .exe:s 执行,而不是从某个父进程创建的。操作系统是windows。

【问题讨论】:

  • 为什么不直接使用事件对象?
  • @wj32: 喜欢互斥锁?它一次只向一个进程发出信号。
  • 这似乎是单个写入器,每个写入器都有多个读取器。这就是你想要的吗?
  • @AareP:event 对象,而不是 mutex。但是看看你的其他 cmets,你可能应该使用 I/O 完成端口或其他东西来排队工作。
  • @AareP:你知道进程的可执行文件名吗?

标签: c++ ipc counter


【解决方案1】:

命名信号量呢? Posix 支持它,不确定 windows。

【讨论】:

  • 带信号量的WaitForSingleObject()调用会减少每个进程的信号量值,一次运行的进程数量是任意的。
【解决方案2】:

考虑您希望分发信息的方式和潜在的重叠 - 如果任何读者完成阅读所需的时间比刷新所需的时间长,那么您将在建议的方法上遇到麻烦。

按照我阅读您问题的方式,有多个读者,作者不知道(或在大多数情况下)一次有多少读者,但想通知读者有新内容可供阅读.

在不知道有多少潜在读者的情况下,您无法使用简单的互斥锁或信号量来了解读者何时完成,也不知道每个人何时完成,您无法获得关于何时重置事件以通知的良好信息用于下一个读取事件。

MS Windows 特定:

共享段
一种选择是将变量放在共享数据段中。这意味着相同的变量可以被所有命名相同段的 exe 读取(和写入),或者如果您将其放入 DLL - 加载共享 DLL。

请参阅http://www.codeproject.com/KB/DLL/data_seg_share.aspx 了解更多信息。

// Note: Be very wary of using anything other than primitive types here!
#pragma data_seg(".mysegmentname") 
HWND hWnd = NULL; 
LONG nVersion = -1;
#pragma data_seg()
#pragma comment(linker, "/section:.mysegmentname,rws")

IPC - COM
将您的主应用程序设为 com 服务,工作人员可以在其中注册事件,将更改推送到每个事件接收器。

IPC - 双事件
假设任何 1 个读取周期远小于写入事件之间的时间。 创建 2 个手动重置事件,在任何时候最多会发出其中 1 个事件的信号,在事件之间交替。信号将立即释放所有读取器,一旦完成,它们将等待备用事件。

【讨论】:

    【解决方案3】:

    您可以通过简单的方式或方式做到这一点 简单的方法是将共享值存储在注册表或文件中,以便所有进程同意经常检查它。

    难的是使用IPC(进程间通信,我最常用的方法是NamedPipes。它不是太难,因为你可以在网上找到很多关于IPC的资源。

    【讨论】:

    • 是的,查找有关 IPC 的信息很容易,要了解它的难点。 :)
    【解决方案4】:

    如果您在 *nix 上,您可以让进程从 named pipe(或套接字)读取,然后在那里写入特定的消息以告诉其他进程它们应该关闭。

    IPC performance: Named Pipe vs Socket

    Windows NAmed Pipes alternative in Linux

    【讨论】:

    • 其实我想知道这些管道是如何工作的。当管道的接收端有多个阅读器时会发生什么?消息会按照先进先出的顺序分发吗?
    • 对于多个阅读器,您应该改用套接字:stackoverflow.com/questions/1634580/…
    【解决方案5】:

    使用命名事件对象手动重置。下面的解决方案比忙着等待更不占用CPU

    发送过程:

    • 设置事件
    • 睡眠 10 毫秒
    • 重置事件

    接收过程:

    • 设置事件后所有等待进程都通过
    • 他们阅读了文件
    • 让他们睡 20 毫秒,所以说不能两次看到相同的事件。
    • 再等等

    Sleep(10) 实际上可能比 Sleep(20) 花费更长的时间,但这只会导致另一个循环(再次读取未更改的文件)。

    【讨论】:

    • 其实我的进程不只是在等待事件。他们还忙于其他事情。所以我不能说他们检查事件状态是否需要 10 毫秒或 1000 毫秒。
    【解决方案6】:

    由于已知可执行文件的名称,我有另一个解决方案,几天前我在一个项目中实现了(在 C# 中): 每个阅读器进程都会创建一个命名事件“Global\someuniquestring_%u”,其中 %u 是它的进程 ID。如果发出事件信号,请读取文件并执行工作。 发送者进程有一个事件句柄列表,如果文件已更改,则将它们设置为活动状态,从而通知所有读取者进程。有时,例如当文件发生变化时,它必须更新事件句柄列表:

    • 获取所有名为“reader.exe”的进程(例如)
    • 为每个进程获取它的 ID
    • 如果是新进程,则为现有事件“Global\someuniquestring_%u”打开一个句柄。
    • 关闭不再运行的进程的所有句柄。

    【讨论】:

      【解决方案7】:

      找到了一种用于监视文件夹更改(使用“event_trigger”-event)并从文件中读取其他事件信息的解决方案:

      HANDLE event_trigger;
      __int64 event_last_time;
      vector<string> event_info_args;
      string event_info_file = "event_info.ini";
      
      // On init
      event_trigger = FindFirstChangeNotification(".", false, FILE_NOTIFY_CHANGE_LAST_WRITE);
      event_last_time = stat_mtime_force("event_info.ini");
      
      // On tick
      if (WaitForSingleObject(event_trigger, 0)==0)
      {
          ResetEventTrigger(event_trigger);
      
          if (stat_mtime_changed("event_info.ini", event_last_time))
          {
              FILE* file = fopen_force("event_info.ini");
      
              char buf[4096];
              assert(fgets(buf, sizeof(buf), file));
              split(buf, event_info_args, "\t\r\n");
              fclose(file);
      
              // Process event_info_args here...
      
              HWND wnd = ...;
              InvalidateRect(wnd,0,false);
          }
      }
      
      // On event invokation
      FILE* file = fopen("event_info.ini", "wt");
      assert(file);
      fprintf(file,"%s\t%s\t%d\n",
          "par1", "par2", 1234);
      fclose(file);
      
      stat_mtime_changed("event_info.ini", event_last_time);
      
      
      // Helper functions:
      void ResetEventTrigger()
      {
          do
          {
              FindNextChangeNotification(evt);
          }
          while(WaitForSingleObject(evt, 0)==0);
      }
      FILE* fopen_force(const char* file);
      {
          FILE* f = fopen(file, "rt");
          while(!f)
          {
              Sleep(10+(rand()%100));
              f = fopen(f, "rt");
          }
          assert(f);
          return f;
      }
      __int64 stat_mtime_force(const char* file)
      {
          struct stat stats;
          int res = stat(file, &stats);
          if(res!=0)
          {
              FILE* f = fopen(file, "wt");
              fclose(f);
              res = stat(file, &stats);
          }
          assert(res==0);
          return stats.st_mtime;
      }
      bool stat_mtime_changed(const char* file, __int64& time);
      {
          __int64 newTime = stat_mtime(file);
          if (newTime - time > 0)
          {
              time = newTime;
              return true;
          }
          return false;
      }
      

      【讨论】:

        猜你喜欢
        • 2014-09-02
        • 2012-01-11
        • 1970-01-01
        • 2011-06-22
        • 1970-01-01
        • 2010-11-23
        • 1970-01-01
        • 2010-12-13
        • 1970-01-01
        相关资源
        最近更新 更多