我应该总是在多线程编程中锁定全局数据吗,为什么或为什么不呢?

比尔·胡

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

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

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

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

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

问题是,转发服务器的性能很差,在receiving-thread写入时锁定了全局列表,但是当时我sending-thread想读取,因此必须等待,但是我认为这种等待是没有用的。

我应该怎么做,我知道全局变量是不好的,但是如果没有全局变量,我该如何同步这两个线程?

我会继续从SO和Google进行搜索。

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

编辑

  1. 对于任何建议,我想知道为什么或为什么不这样做,请给我原因,非常感谢。
伊万·奥坎普(Iwan Aucamp)

笔记:

  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. 还有原子容器的选项,请看:

  6. 您还可以采用boost :: asio的异步方法-尽管正确完成您的案例应该很快。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

具有@Value注释和单个方法的字段。我应该让它们都静止吗?为什么/为什么不呢?

如果函数不使用全局数据,为什么在机器代码级别上线程安全?

为什么我的球雪碧不总是在我的雪碧棒上反弹?

为什么多线程在我的代码中效率不高?

为什么我的多线程效率不高?

我应该在多线程范例中锁定数据表吗?

为什么我的ubuntu ip总是在变化?

Android版式:为什么我的按钮总是在ListView的下面?

为什么我的momentjs总是在03显示分钟?

为什么不总是在Java中始终使用ArrayList而不是普通的ol'数组?

使用std名称空间,为什么(或为什么不)我应该在std ::前面加上前缀?

为什么我在此多线程处理中始终以空白数据结尾

为什么在If中总是在someArray中对someVar进行赋值

为什么几乎总是在类中Set之前先编写Get,这是有原因的吗?

我应该总是在android中的单独线程中执行数据库操作吗?

PHP为什么我的全局数组在扩展类中为空

为什么TODO总是在vim中突出显示?

为什么总是在JavaScript中声明函数?

为什么这个项目总是在Makefile中执行?

为什么总是在 blazor 中调用“HandleValidSubmit()”

Django manage.py自定义命令可以返回值吗?如何,或为什么不呢?

为什么“ _”不能总是在交互式shell中给我最后的结果

为什么我总是在mscor.lib中得到“ System.EntryPointNotFoundException”?

为什么我不能总是在Pidgin中要求重新授权?

为什么我总是在Codeigniter.2.1.2中得到表会话的重复值?

为什么我的服务器总是在文件夹权限中自动更改?

为什么我总是在 SparkJava 中获得一个新会话?

为什么 enum 数据类型总是在 c 中的 main() 之外声明?

为什么选项的位置总是在变化?