【问题标题】:C++ objects in multithreading多线程中的 C++ 对象
【发布时间】:2011-07-20 20:54:41
【问题描述】:

当一个类的单个实例/对象在不同线程之间共享时,我想询问 C++ 中的线程安全(例如使用带有 C++ 包装器的 POSIX 线程)。例如,A 类的单个对象的成员方法将在不同的线程中调用。关于线程安全,我应该/可以做些什么?

class A {
    private:

    int n;

    public:

    void increment()
    {
        ++n;
    }

    void decrement()
    {
        --n;
    }
};
  • 我应该在递增/递减方法中使用锁或其他东西来保护类成员 n 吗?静态(类变量)成员也需要锁吗?
  • 如果成员是不可变的,我不必担心,对吧?
  • 有什么我现在无法预见的吗?

除了多线程中的单个对象的场景,多线程的多个对象呢?每个线程拥有一个类的实例。除了静态(类变量)成员之外还有什么特别的吗?

这些都是我的想法,但我相信这是一个很大的话题,如果你有很好的资源并参考之前的讨论,我会很高兴。

问候

【问题讨论】:

    标签: c++ multithreading thread-safety


    【解决方案1】:

    建议:不要尝试手动操作。使用一个好的多线程库,比如来自 Boost 的那个:http://www.boost.org/doc/libs/1_47_0/doc/html/thread.html

    这篇来自英特尔的文章会给你一个很好的概述:http://software.intel.com/en-us/articles/multiple-approaches-to-multithreaded-applications/

    【讨论】:

      【解决方案2】:

      这是一个非常大的话题,可能不可能在这个线程中完成这个话题。 黄金法则是“你不能在别人写作的时候阅读”。 因此,如果您有一个共享变量的对象,则必须在访问共享变量的函数中加锁。

      很少有这种情况不是真的。 第一种情况是整数,你可以使用 c-smile 所示的原子函数,在这种情况下 CPU 将在缓存上使用硬件锁,因此其他内核无法修改变量。 第二种情况是无锁队列,它是一种特殊的队列,它使用比较和交换功能来保证指令的原子性。

      所有其他情况都必须锁定... 第一种方法是锁定所有内容,当涉及更多对象时,这可能会导致很多问题(ObjA 尝试从 ObjB 读取,但是,ObjB 正在使用该变量并且还在等待等待 ObjA 的 ObjC)循环锁可能导致无限期等待(死锁)。

      更好的方法是最小化线程共享变量的点。 例如,如果你有一个数据数组,并且你想并行计算数据,你可以启动两个线程,线程一将仅在偶数索引上工作,而线程二将在奇数索引上工作。该线程正在处理同一组数据,但只要数据不重叠,您就不必使用锁。 (这称为数据并行化)

      另一个方法是将应用程序组织为一组“工作”(在线程上运行的函数产生结果)并使工作仅与消息通信。你只需要实现一个线程安全的消息系统和一个工作调度器就可以了。或者你可以使用像 intel TBB 这样的 libray。

      这两种方法都不能解决死锁问题,但可以让您隔离问题并更轻松地发现错误。多线程中的 bug 确实很难调试,有时也很难找到。

      所以,如果你正在学习,我建议从 thery 开始,然后从 pThread 开始,那么当你学会了基础移动到更用户友好的库时,比如 boost,或者如果你使用 Gcc 4.6 作为编译器 C++ 0x std::thread

      【讨论】:

        【解决方案3】:
        • 是的,如果函数在多线程环境中使用,您应该使用锁来保护它们。你可以使用boost libraries

        • 是的,不可变成员不应该成为问题,因为这样的成员一旦被初始化就无法更改。

        关于“具有多个线程的多个对象”.. 这在很大程度上取决于您想要做什么,在某些情况下,您可以使用 thread pool 这是一种机制,它具有定义数量的线程等待作业进来吧。但是那里没有thread concurrency,因为每个线程都做一项工作。

        【讨论】:

          【解决方案4】:

          你必须保护柜台。没有其他选择。

          在 Windows 上,您可以使用以下函数执行此操作:

          #if defined(PLATFORM_WIN32_GNU)
          
            typedef long counter_t;
            inline long _inc(counter_t& v)               {    return InterlockedIncrement(&v);  }
            inline long _dec(counter_t& v)               {    return InterlockedDecrement(&v);  }
            inline long _set(counter_t &v, long nv)      {    return InterlockedExchange(&v, nv);  }
          
          #elif defined(WINDOWS) && !defined(_WIN32_WCE) // lets try to keep things for wince simple as much as we can
          
            typedef volatile long counter_t;
            inline long _inc(counter_t& v)               {    return InterlockedIncrement((LPLONG)&v);  }
            inline long _dec(counter_t& v)               {    return InterlockedDecrement((LPLONG)&v);  }
            inline long _set(counter_t& v, long nv)      {    return InterlockedExchange((LPLONG)&v, nv);  }
          

          【讨论】:

            猜你喜欢
            • 2014-12-27
            • 2010-09-15
            • 1970-01-01
            • 1970-01-01
            • 2014-10-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多