【发布时间】:2019-07-15 04:39:53
【问题描述】:
我有一个有点独特和有趣(和可怕)的场景,它迫使我做一些棘手的事情。问题如下:
- 我们需要一种原子类型来在实时线程和后台加载线程之间进行无锁同步。
-
类型(不幸的是)必须编译:
一个。 QNX只有c++03,但支持
boost::atomic。b.具有 c++11 但无法构建的 Nucleus
boost::atomic
这迫使我考虑同时使用boost::atomic 和std::atomic。我解决这个问题的方法是生成一个新类型,将所有功能转发到平台上的相关atomic 类型。这个想法是这样的:
Atomic.hpp
namespace osal { namespace detail {
template <typename T, template <class> class TAtomic >
struct AtomicImpl {
void store(T desired, memory_order order = osal::memory_order_seq_cst)
{
_atomic.store(desired, order);
}
// ... Other api
private:
AtomicImpl& operator=(const AtomicImpl& rhs);
// ... Other blocked operations
TAtomic<T> _atomic;
};
}}
#if defined QNX
#include <osal/QNX/Atomic.hpp>
#elif defined NUCLEUS
#include <osal/NUCLEUS/Atomic.hpp>
#endif
这列出了一个类,该类将使用正确的 API 转发到 atomic 类型。幸运的是,boost 和 std 实现几乎完全匹配。
然后在每个单独的操作系统文件中,如下所示:
Nucleus/Atomic.hpp
#include <atomic>
namespace osal
{
template <typename T> struct atomic
{
typedef detail::AtomicImpl<T, std::atomic> type;
};
}
这将创建带有std::atomic 的原子类型,允许使用如下:
osal::atomic<uint8_t>::type a;
a.store(1);
为了处理内存顺序的概念,存在一个类似的系统,其中包含一个文件,可以使用预处理器委托给每个操作系统实现。在实现中,例如。 Nucleus,我们有这个:
Nucleus/MemoryOrder.hpp
#include <memory>
namespace osal {
using std::memory_order;
using std::memory_order_relaxed;
...
}
显然,等效的using boost::... 将在 QNX 文件中(幸运的是它们再次匹配!)。
问题
这似乎有效。我可以制作一个原子并对其进行操作。我有一个顾虑:
这还是原子的吗?
既然我们已经在 API 中引入了间接,那么对原子对象的调用顺序会不会有问题?
作为一个额外的问题,考虑到限制,有没有更好的方法来做到这一点?
【问题讨论】:
-
w.r.t. atomic (没有那么多memoryorder)你可以查看生成的代码,看看它是否实际上是原子的。 (这样做时可能使用最严格的记忆顺序。
-
这些库不能利用 API 本身的任何编译器魔法,所以只要你的 API 与两个库中使用的类型相同,我看不出它不应该工作的原因.添加一个函数调用层在原子性方面应该没有任何作用。
-
@MaxLanghof 我是这么认为的,我还认为即使外围操作不是原子的,也没关系,因为内部调用是。但我想检查一下。
-
肯定会的。另一方面,它比维护自己的包装更省力。我还可以考虑中间解决方案,例如为从 std::atomic 或 boost::atomic 公开继承的 atomic 定义包装器。我很确定你的解决方案是安全的,但它看起来太冗长了。
-
@AlexanderGutenev 从
std::atomic继承不是一个好主意,它没有虚拟析构函数,如果人们将它用作std类型指针,可能会导致非常糟糕的事情发生。我认为封装 API 并没有那么糟糕,它给了我们一点灵活性。
标签: c++ multithreading c++11 boost thread-safety