【问题标题】:multithreading, inter-thread communication, synchronization多线程,线程间通信,同步
【发布时间】:2012-03-06 23:21:49
【问题描述】:

一个类 XYZ 有 3 个成员变量 x、y、z。有 [N] 个 XYZ 对象的向量。

有 3 个线程 A、B、C,它们可以访问向量中的任何对象以及该对象的任何成员变量。

class XYZ
{
public:
double x;
double y;
double z;
};

std::vector 有 N 个元素,其中 N 在整个程序中是固定的。

如何设计线程间通信以实现线程安全和最大效率,即最小阻塞。

这是我的一些思考过程,如果我错了,请纠正我

  1. 将向量分成更小的向量并封装到每个线程中 类,然后使用消息队列来传递数据。问题 所有 3 个线程都可以访问向量和对象的任何位置 成员,因此难以细分和封装。 消息队列本身需要阻塞,即需要读取器 发件人添加到队列时被阻止。
  2. 使用原子库使访问原子化,因此避免 阻塞。问题是原子性的,依赖于操作系统,即一些 操作在Linux下被认为是原子的,在Linux下可能不是原子的 窗户。
  3. 互斥体,为每个成员变量添加3个互斥体对象,例如,mutex_x, 互斥量_y,互斥量_z。但是,问题是互斥锁是不可复制的,

也就是说,如果我们有一个类似的类,

class XYZ_mutex
{
public:
double x;
double y;
double z;
boost::mutex mutex_x;
boost::mutex mutex_y;
boost::mutex mutex_z;
};

我们不能有 XYZ_mutex 的向量,因为 .push_back() 是一个复制构造函数。

谢谢。

【问题讨论】:

  • 为什么要使用三个互斥锁而不是一个?而且你关于没有向量的逻辑是错误的——你可以为你的类创建一个复制构造函数(它当然不会复制互斥体,但你为什么要这样做呢?)
  • 虽然“我们不能拥有 XYZ_mutex 的向量,因为 .push_back() 是一个复制构造函数。”是的,你可以只拥有ZYX_mutex * 的向量。
  • 线程 A、B、C 对向量做了什么?
  • 您还没有定义“访问”。是读访问、写访问还是两者兼而有之?
  • 效率问题可能无法在此级别解决。您的 XYZ 将进行什么样的操作?该问题的答案应该决定您的共享/分发设计。

标签: c++ multithreading


【解决方案1】:

您需要考虑您的程序有哪些使用模式和一致性要求。

第一个也是最重要的使用模式是是否有任何线程需要修改这些结构。如果没有,您实际上不需要任何锁定 - 只需确保在线程开始读取结构之前填充结构即可。

如果线程确实要修改结构,则需要考虑一致性。您需要问自己,除了修改单个 double 值之外,是否存在任何限制,即一个线程可以看到另一个线程所做的更改。

一旦你定义了它,开始考虑是否可以以任何方式在线程之间拆分数据以减少线程之间的冲突 - 即使它不能完全消除,回答是否保留三个双精度向量是否更好的问题, XYZ 或其他组织的向量取决于是否会有一些线程更频繁地访问某些 XYZ 对象,或者是否会有线程更频繁地访问 x 成员,而其他线程更频繁地访问 ys 或 zs 等等。

如果您不能说任何线程访问任何实例的任何成员的概率,那么很难说如何最好地组织它们。将它们放在堆上以便它们位于单独的缓存行中甚至可能是一个好主意。

总的来说,最好的建议可能是首先将它们放入最适合您需求的任何数据结构(向量、映射、集合等),使用单个互斥锁来同步整个事物,编写程序,然后测试您是否遇到瓶颈。

【讨论】:

    【解决方案2】:

    我会选择选项 1(进行一些更改,见下文),仅仅是因为为数组中的每个项目设置一个互斥锁成本太高。

    因此,将数组分成更小的块是个好主意,每个块都受互斥体保护。然后,当一个线程需要访问数组的某个部分时,它可以访问另一个数据结构,该结构为它提供了它需要根据访问项目所在的时间间隔锁定的互斥锁(例如关联索引的哈希表具有该特定时间间隔的互斥锁的项)。

    【讨论】:

    • 如果他可以隔离访问,这可能是个好主意。但是,如果一个线程需要访问一个部分以更新另一个部分,则它需要获取两个锁,并且避免死锁是很棘手的(尽管可以做到)。
    【解决方案3】:

    没有一个正确的答案。基本规则是:

    1. 尝试组织事物,使每个线程都需要一个以上 互斥体。否则,您需要一些(通常不是显而易见的)解决方案来避免 死锁的风险。

    2. 尽量让每个互斥锁保持尽可能短的时间。如果你可以的话 组织事物,以便有 n 个不同的数组,每个线程只 访问一个不同的数组,每个不同的数组只 由单个类访问,这是最优的,因为这意味着每个 线程可以在没有锁的情况下运行,一旦它获得了它的所有权 不同的数组。 (不过,听起来情况并非如此。)

    很大程度上取决于线程如何(以及多久)访问 向量的元素。

    最后,有几种处理互斥量问题的方法 XYZ 对象。最明显的是使用最近的编译器,它 支持移动语义; std::mutex 可移动,可用于 std::vector。否则,您可以使用boost::shared_ptr<> 互斥体。

    【讨论】:

    【解决方案4】:

    使用选项 1。但也将向量分成 3 部分,并允许每个线程处理其块,以免干扰其他线程。您始终可以使用向量的长度来创建这些虚拟边界。哎呀,你可以让每个线程都有自己的向量。然后,当您插入向量时,通过“负载均衡器”发送您的请求,以便它插入正确的向量(只是贪婪)。您还可以使用平衡器重新平衡向量,因为某些线程比其他线程慢(将项目从 1 个向量移动到其他向量)

    【讨论】:

      猜你喜欢
      • 2011-02-11
      • 2020-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多