【问题标题】:avoid race condition in android native code (C++)避免 android 本机代码 (C++) 中的竞争条件
【发布时间】:2014-04-29 15:19:13
【问题描述】:

问题:对同一个 unsigned int 变量执行按位操作(清除两个不同位位置)的两个线程正在创建竞争条件。 (它是一个标志变量)。

目前正在尝试解决方案:每次在设置或清除位位置之前使用互斥锁。

环境:Android 用户空间、仿生、C++

现在,我也想检查一下,我是否可以使用 android 的原子操作函数来避免竞争条件, android_atomic_and/android_atomic_or 并将变量定义为 volatile unsigned int。 据我了解,这在逻辑上类似于atomic_setatomic_and 等内核空间原子操作。

所以,如果有人可以解决我的疑问,或者是否有其他解决方案可以更好地解决此问题,请提供帮助。

【问题讨论】:

  • volatile 暗示原子性。除此之外,您似乎对解决方案有一些想法,尽管您可能会考虑是否可以简单地将两个标志分开到它们自己的变量/记忆词中。
  • @ChrisStratton,该变量中的每一位代表一个标志,因此该变量(例如 mFlags)将包含多达 32 个标志条件,并且线程将访问同一个 mFlag 变量以设置和清除相应的位位置更新状态。
  • 鉴于您有多个线程,位图这些标志可能是一个糟糕的设计决策,除非您愿意使用互斥锁或某种本土同步设置器来保护它们。将它们分成 32 个字大小的变量可能会简单得多。
  • android_atomic_and/android_atomic_or 应该做你想做的事。您不必将 int 变量声明为 volatile 即可使用这些函数。但是要读取变量的值,您可能需要使用 android_atomic_release_load() 之类的东西。

标签: android c++ c multithreading race-condition


【解决方案1】:

对于这个问题,一如既往有许多不同的解决方案。 在大多数系统上,基本整数操作通常是原子的。但是你必须确保变量没有被缓存,并且基本操作可以一步完成。因此将整数设置为 0 将是这样一个基本操作。但是将它的值增加一,或者在某处改变一点不是。因为您正在读取当前状态,计算新状态,最后将其写回。另一个线程同时做同样的事情会导致不一致的状态。 (例如一个变量增加了两次,但它的值只增加了一次)

一种解决方案是用锁包围对该整数的访问。

另一种更快的方法是使用 compare_and_set 方法进行写入。 (阅读应该没问题)在这种情况下,请确保您的变量没有缓存在其中一个 CPU 中,例如,将其声明为 volatile。 这可能对您有所帮助;

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...) type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...) These builtins perform an atomic compare and swap. That is, if the current value of *ptr is oldval, then write newval into *ptr. The “bool” version returns true if the comparison is successful and newval was written. The “val” version returns the contents of *ptr before the operation.

我有这部分:http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

使用正确的比较和设置,您可以确保变量只被修改,如果没有其他人同时这样做。因此,您需要在 while 循环中使用它,如此处的线程所示:How to compare and swap atomically in ARM7?

第三种方法是前往一个库,它为您提供许多不同的“原子操作”的长列表。这可能是最干净的方式。 使用 volatile 和 compare-and-set 自己进行处理可能很危险,因为如果变量的特殊处理在某处被遗忘,它不会警告您或其他人。

最后一件事。请注意,未受保护的读取访问仅适用于单个整数变量。如果您使用双精度数或两个整数,您可能会得到不一致的状态。在进程 0 的写入过程中,双变量的一半(或 2 的一个整数)已经具有新值,而另一部分仍然是旧值,而您在线程 1 中读取它。这将直接导致难以找到漏洞。很少发生,但一旦发生,就会发生灾难。 (这部分仅适用于常见的 32 位 CPU 系统。某些较小的系统可能会以较小的块处理内存。另一方面,64 位系统通常将两个整数(64 位)作为一个块处理,它可以使用双精度数,但不能使用更大的结构)

祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-30
    • 2010-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    • 2019-06-12
    相关资源
    最近更新 更多