在线程

时间:2015-09-14 14:39:50

标签: c++ multithreading thread-safety volatile

tl;博士:

 class Controller
 {
 public:
    volatile Netconsole* nc;
    void init();        //initialize the threads
    void calculate();   // handler for the "mothership app"
    void senderThreadLoop();  //also calls reinitNet() if connection is broken.
    void listenerThreadLoop();
    inline void reinitNet(){ delete nc; nc = new Netconsole(); }
 }

//里面     Json :: Value header = nc-> Recv();

error: passing 'volatile Netconsole' as 'this' argument discards qualifiers [-fpermissive]

如果重新实例化实用程序类,则必须在两个线程内更新两个线程之间共享的实用程序类(Netconsole)实例的指针,但将其声明为volatile会生成上述错误。如果它在一个线程内部更新,则另一个线程仍可能使用旧的无效指针。如何确保它们都更新,但通过指针使用方法不会触发上述错误?

扩展信息:

"智能胶水逻辑"库我写的用于在第三方软件和自定义设备之间传递和转换消息。它由三个基本线程组成:

  • 一个处理程序:第三方应用程序的主线程定期调用"计算"在我的库中处理新的更新 - 要发送的数据,接收的数据
  • 发送方线程,用于转换并发送处理程序推送到发送缓冲区的任何内容
  • 一个侦听器线程,用于将从设备接收的任何数据转换并推送到接收缓冲区。

发送方和侦听器线程都使用相同的实用程序类来处理与设备的网络通信;在初始化时,类创建与设备的连接,并且两个线程分别执行阻塞读取或等待新数据发送。如果有任何问题,发送方线程执行所有"维护"当侦听器线程进入等待返回连接的安全状态时工作。

现在,由于两个线程共享一个到设备的连接,它们都共享通信类的同一个实例,作为指向该类的指针。

问题出在重新连接的过程中 - 它涉及破坏和创建帮助器类实例,利用析构函数和构造函数中已经存在的安全关闭和初始化。结果指针改变了。没有volatile,听众很可能不会收到更新的指针。随着不稳定,它会不必要地抗议,因为nc(指针)在随机时刻不会发生变化 - 首先会向听众发出问题通知,然后它进入一个安全状态,在那里它不会出现问题。在' nc'上执行任何操作并通知发件人已做好准备。只有这样,发送方才会执行修复并通知监听器恢复正常操作。

那么在这种情况下,什么是正确的解决方案?

1 个答案:

答案 0 :(得分:1)

您需要的是一系列操作。生产线程有2个相关的操作:"初始化新的Netconsole"和#34;写指针"。消费线程还有两个操作:"读指针"和"使用新的Netconsole对象"。这4个操作必须按完全顺序排序,以使更新可见。

到目前为止,实现这一目标的最简单方法是两个内存障碍。写入屏障(指针写入时为std::memory_order_release)可防止前两个操作被重新排序,并且读取屏障(指针加载上的std::memory_order_acquire)可防止最后两个操作被重新排序。

由于两个线程独立运行,因此程序的正确性不应取决于特定对象更新是否在特定对象使用之前发生。更新线程可能只是有点慢,这不应该破坏你的程序。所以写和读之间的第三个顺序并不是真的相关,你不应该试着修复"它。

总结:是的,4个操作必须以完全正确的顺序发生,以使结果可见,但如果第二个和第三个操作是 重新排序,然后更新完全不可见到消费线程。它是一个原子更新,全部或全部。

仍然需要清理旧物体。生产线程不能只假设消费线程已经看到指针更新。必须有同步以确保两个线程都同意旧对象未使用。最简单的是,如果生成的线程在创建新对象之后严格不使用旧对象(内存屏障在这里有帮助),并且消费线程在知道那里有新对象时立即清理旧对象。 (因为这种情况严格地发生在读屏障之后,因此在写屏障之后,反过来在生产线程的最后一次使用之后)