0

我是多线程编程的新手(实际上,我不是多线程的新手,但我总是使用全局数据来读写线程,我认为这让我的代码变得又丑又慢,我很渴望提高我的技能)

我现在正在使用 c++ 开发一个转发服务器,为了简化问题,我们假设只有两个线程,一个接收线程和一个发送线程,而且,像往常一样愚蠢的设计,我有一个全局 std::保存数据的列表:(

receiving-thread从服务器读取原始数据并将其写入全局 std::list。

sending-thread读取全局 std::list 并将其发送给多个客户端。

pthread_mutex_lock用来同步全局 std::list。

问题是转发服务器的性能很差,写时全局列表被锁定receiving-thread,但当时我sending-thread想读,所以它必须等待,但我认为这种等待是无用的。

我该怎么办,我知道全局不好,但是,没有全局,我如何同步这两个线程?

我会继续从 SO 和谷歌搜索。

任何建议、指南、技术或书籍将不胜感激。谢谢!

编辑

  1. 对于任何建议,我想知道为什么或为什么不,请告诉我原因,非常感谢。
4

1 回答 1

1

笔记:

  1. 请提供更完整的示例:http ://sscce.org/

答案:

  1. 是的,您应该同步对共享数据的访问。

    • 注意:这是对 std::list 实现的假设 - 这可能适用于您的情况,也可能不适用 - 但由于此假设对某些实现有效,您不能假设您的实现必须是线程安全的,没有一些明确的保证
    • 考虑一下片段:

      std::list g_list;
      
      void thread1()
      {
          while( /*input ok*/ )
          {
              /*read input*/
              g_list.push_back( /*something*/ );
          }
      }
      
      void thread2()
      {
          while( /*something*/ )
          {
              /*pop from list*/
              data x = g_list.front();
              g_list.pop_front();
          }
      }
      
    • 比如说列表中有 1 个元素
    • std::list::push_back() 必须这样做:
      • 分配空间(许多 CPU 指令)
      • 将数据复制到新空间(许多 CPU 指令)
      • 更新前一个元素(如果存在)以指向新元素
      • 设置 std::list::_size
    • std::list::pop_front() 必须这样做:
      • 可用空间
      • 将下一个元素更新为没有前一个元素
      • 设置 std::list_size
    • 现在说线程 1 调用 push_back() - 在检查是否存在元素(检查大小)之后 - 它继续更新这个元素 - 但在此之后 - 在它有机会更新元素之前 - 线程 2 可能正在运行 pop_front - 并忙于释放第一个元素的内存 - 这可能导致线程 1 导致分段错误 - 甚至内存损坏。同样,大小的更新可能会导致 push_back 胜过 pop_front 的更新 - 然后当您只有 1 个元素时,您的大小为 2。
  2. 不要在 C++ 中使用 pthread_* 除非你真的知道你在做什么 - 使用 std::thread (c++11) 或 boost::thread - 或者自己将 pthread_* 包装在一个类中 - 因为如果你不考虑异常你最终会陷入僵局

  3. 在此特定示例中,您无法通过某种形式的同步 - 但您可以优化同步

    1. 不要将数据本身复制进和复制出 std::list - 复制指向数据的指针进出列表
    2. 仅在您实际访问 std::list 时锁定 - 但不要犯此错误:

      {
          // lock
          size_t i = g_list.size();
          // unlock
          if ( i )
          {
              // lock
              // work with g_list ...
              // unlock
          }
      }
      
  4. 这里更合适的模式是消息队列——您可以使用互斥体、列表和条件变量来实现。以下是您可以查看的一些实现:

  5. 还有原子容器的选项,看看:

    • http://calumgrant.net/atomic/ - not sure if this is backed by actual atomic storage (as opposed to just using synchronization behind an interface)
    • google for more
  6. You could also go for an asynchronous approach with boost::asio - though your case should be quite fast if done right.

于 2013-07-15T22:36:32.083 回答