使用 Boost 进程间代码时的高 CSwitch(“上下文切换”)(在 Windows、Win32 上)

发布于 2024-08-07 02:57:09 字数 3010 浏览 4 评论 0原文

我正在编写一个多线程应用程序。

我使用的是 boost::interprocess 类(版本 1.36.0)

本质上,我有工作线程,当它们可以做工作时需要得到通知。

我尝试了“信号量”和“条件”方法。

在这两种情况下,工作线程的 CSwitch(上下文切换)似乎都非常高,比如每秒 600 次切换。

我看了一下代码,看起来它只是检查一个标志(原子地使用互斥体),然后在下次重试之前生成时间片。

我期望代码使用 WaitForSingleObject 或其他东西。

讽刺的是,这正是我在决定“正确”做并使用 Boost 之前所做的事情! (即使用互斥体定期检查标志的状态)。唯一的区别是,在我的方法中,我在两次检查之间睡眠了 50 毫秒,因此我没有出现高 CSwitch 问题(是的,工作在 50 毫秒内不会开始,这对我来说很好)。

几个问题:

  1. 这个“高”CSwitch 值重要吗?
  2. 如果 boost 库使用 CRITICAL_SECTIONS 而不是信号量(我不关心进程间同步 - 所有线程都在同一进程中),是否会发生这种情况?
  3. 如果 boost 使用 WaitForSingleObject 会发生这种情况吗?
  4. Boost 库中是否有另一种方法使用上述 Win32 等待方法 (WaitForXXX),我认为该方法不会受到此 CSwitch 问题的影响。

更新:这是一个伪代码示例。我无法添加真正的代码,因为它会有点复杂。但这几乎就是我正在做的事情。这只是启动一个线程来执行一次性异步活动。

注意:这些只是插图!此示例中缺少负载,例如,如果您在线程达到“等待”之前调用injectWork(),它将无法工作。我只是想说明一下我对 boost 的使用。

用法类似于:

int main(int argc, char** args)
{
  MyWorkerThread thread;
  thread.startThread();

  ...

  thread.injectWork("hello world");
}

这是使用 boost 的示例。

class MyWorkerThread
{
public:

  /// Do work asynchronously
  void injectWork(string blah)
  {
    this->blah = blah;

    // Notify semaphore
    this->semaphore->post();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    // Wait for semaphore to be invoked
    semaphore->wait();

    cout << blah << endl;
  }

  string blah;  
  boost::interprocess::interprocess_semaphore* semaphore;
};

这是我的“天真的”投票代码:

class MyWorkerThread_NaivePolling
{
public:

  MyWorkerThread_NaivePolling()
  {
    workReady = false;
  }

  /// Do work asynchronously
  void injectWork(string blah)
  {
    section.lock();

    this->blah = blah;
    this->workReady = true;

    section.unlock();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 

  /// Uses Win32 CriticalSection
  class MyCriticalSection
  {
    MyCriticalSection();
    void lock();
    void unlock();
  };

  MyCriticalSection section;


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    while (true)
    {
      bool myWorkReady = false;
      string myBlah;

      // See if work set 
      section.lock();
      if (this->workReady)
      {
        myWorkReady = true;
        myBlah = this->blah;
      }
      section.unlock();

      if (myWorkReady)
      {
        cout << blah << endl;
        return;
      }
      else
      {
        // No work so sleep for a while
        Sleep(50);
      }
    }
  }

  string blah;  
  bool workReady;
};

干杯,

约翰

I'm writing a multithreaded app.

I was using the boost::interprocess classes (version 1.36.0)

Essentially, I have worker threads that need to be notified when work is available for them to do.

I tried both the "semaphore" and "condition" approaches.

In both cases, the CSwitch (context switch) for the worker threads seemed very high, like 600 switches per second.

I had a gander at the code and it seems like it just checks a flag (atomically using a mutex) and then yields the timeslice before trying again next time.

I was expecting the code to use WaitForSingleObject or something.

Ironically, this was exactly how I was doing it before deciding to do it "properly" and use Boost! (i.e. using a mutex to check the status of a flag regularly). The only difference was, in my approach I was sleeping like 50ms between checks so I didn't have the high CSwitch problem (and yes it's fine for me that work won't start for up to 50ms).

Several questions:

  1. Does this "high" CSwitch value matter?
  2. Would this occur if the boost library was using CRITICAL_SECTIONS instead of semaphores (I don't care about inter-process syncing - all threads are in same process)?
  3. Would this occur if boost was using WaitForSingleObject?
  4. Is there another approach in the Boost libs that uses the aforementioned Win32 wait methods (WaitForXXX) which I assume won't suffer from this CSwitch issue.

Update: Here is a pseudo code sample. I can't add the real code because it would be a bit complex. But this is pretty much what I'm doing. This just starts a thread to do a one-off asynchronous activity.

NOTE: These are just illustrations! There is loads missing from this sample, e.g. if you call injectWork() before the thread has hit the "wait" it just won't work. I just wanted to illustrate my use of boost.

The usage is something like:

int main(int argc, char** args)
{
  MyWorkerThread thread;
  thread.startThread();

  ...

  thread.injectWork("hello world");
}

Here is the example using boost.

class MyWorkerThread
{
public:

  /// Do work asynchronously
  void injectWork(string blah)
  {
    this->blah = blah;

    // Notify semaphore
    this->semaphore->post();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    // Wait for semaphore to be invoked
    semaphore->wait();

    cout << blah << endl;
  }

  string blah;  
  boost::interprocess::interprocess_semaphore* semaphore;
};

And here was my "naive" polling code:

class MyWorkerThread_NaivePolling
{
public:

  MyWorkerThread_NaivePolling()
  {
    workReady = false;
  }

  /// Do work asynchronously
  void injectWork(string blah)
  {
    section.lock();

    this->blah = blah;
    this->workReady = true;

    section.unlock();
  }

  void startThread()
  {
    // Start the thread (Pseudo code)
    CreateThread(threadHelper, this, ...);
  }

private: 

  /// Uses Win32 CriticalSection
  class MyCriticalSection
  {
    MyCriticalSection();
    void lock();
    void unlock();
  };

  MyCriticalSection section;


  static void threadHelper(void* param)
  {
    ((MyWorkerThread*)param)->thread();
  }

  /// The thread method
  void thread()
  {
    while (true)
    {
      bool myWorkReady = false;
      string myBlah;

      // See if work set 
      section.lock();
      if (this->workReady)
      {
        myWorkReady = true;
        myBlah = this->blah;
      }
      section.unlock();

      if (myWorkReady)
      {
        cout << blah << endl;
        return;
      }
      else
      {
        // No work so sleep for a while
        Sleep(50);
      }
    }
  }

  string blah;  
  bool workReady;
};

Cheers,

John

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情释 2024-08-14 02:57:09

在非 POSIX 系统上,似乎 interprocess_condition 是使用循环模拟的,正如您在问题中所描述的那样。并且 interprocess_semaphore 使用互斥体和 interprocess_condition 进行模拟,因此 wait()-ing 最终会进入同一个循环。

既然您提到不需要进程间同步,您应该查看 Boost.Thread,它提供了 条件变量。有趣的是,它似乎是在 Windows 上实现的“经典< /a>”方式,使用...信号量。

On non-POSIX systems, it seems that interprocess_condition is emulated using a loop, as you describe in your in question. And interprocess_semaphore is emulated with a mutex and an interprocess_condition, so wait()-ing ends up in the same loop.

Since you mention that you don't need the interprocess synchronization, you should look at Boost.Thread, which offers a portable implementation of condition variables. Amusingly, it seems that it is implemented on Windows in the "classical" way, using a... Semaphore.

梦罢 2024-08-14 02:57:09

如果您不介意特定于 Windows(Windows 上的较新版本),请检查 轻量条件变量CONDITION_VARIABLE(如关键部分):

If you do not mind a Windows specific (newer versions on windows), check the link for light weight condition variables CONDITION_VARIABLE (like critical sections):

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文