Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何编写安全的原子对象包装器?_C++ - Fatal编程技术网

C++ 如何编写安全的原子对象包装器?

C++ 如何编写安全的原子对象包装器?,c++,C++,我一直在尝试编写一个包装类来包装Win32内部函数,例如InterlockedIncrement,InterlockedExchange。虽然我的问题可能与其他支持类似本质的平台类似 我有一个基本的模板类型: template <typename T, size_t W = sizeof(T)> class Interlocked {}; 模板 类互锁{}; 它部分专用于不同大小的数据类型。例如,下面是32位的: // // Partial specialization for

我一直在尝试编写一个包装类来包装Win32内部函数,例如
InterlockedIncrement
InterlockedExchange
。虽然我的问题可能与其他支持类似本质的平台类似

我有一个基本的模板类型:

template <typename T, size_t W = sizeof(T)>
class Interlocked {};
模板
类互锁{};
它部分专用于不同大小的数据类型。例如,下面是32位的:

//
// Partial specialization for 32 bit types
//
template<typename T>
class Interlocked <T, sizeof(__int32)>
{
public:

    Interlocked<T, sizeof(__int32)>() {};

    Interlocked<T, sizeof(__int32)>(T val) : m_val(val) {}

    Interlocked<T, sizeof(__int32)>& Interlocked<T, sizeof(__int32)>::operator= (T val)
    {
        InterlockedExchange((LONG volatile *)&m_val, (LONG)val);
        return *this;
    }

    Interlocked<T, sizeof(__int32)> Interlocked<T, sizeof(__int32)>::operator++()
    {
        return static_cast<T>(InterlockedIncrement((LONG volatile *)&m_val));
    }

    Interlocked<T, sizeof(__int32)> Interlocked<T, sizeof(__int32)>::operator--()
    {
        return static_cast<T>(InterlockedDecrement((LONG volatile *)&m_val));
    }

    Interlocked<T, sizeof(__int32)>& Interlocked<T, sizeof(__int32)>::operator+(T val)
    {
        InterlockedExchangeAdd((LONG volatile *)&m_val, (LONG) val);
        return *this;
    }

    Interlocked<T, sizeof(__int32)>& Interlocked<T, sizeof(__int32)>::operator-(T val)
    {
        InterlockedExchangeSubtract((LONG volatile *)&m_val, (LONG) val);
        return *this;
    }

    operator T()
    {
        return m_val;
    }

private:

    T m_val;
};
//
//32位类型的部分专门化
//
模板
类联锁
{
公众:
互锁(){};
联锁(T val):m_val(val){}
联锁和联锁::运算符=(T val)
{
联锁交换((长波动*)和m_val,(长)val);
归还*这个;
}
联锁联锁::运算符++()
{
返回静态转换(联锁增量((长时间波动*)&m值));
}
联锁的:操作员--()
{
返回静态_cast(联锁减量((长volatile*)和m_val));
}
联锁和联锁::操作员+(T val)
{
InterlocatedExchangeAdd((长)volatile*)&m_val,(长)val);
归还*这个;
}
联锁和联锁::操作员-(T val)
{
联锁交换减法((长可变*)和m_val,(长)val);
归还*这个;
}
算子T()
{
返回m_val;
}
私人:
T m_val;
};

然而,我得出的结论是,我不知道如何安全地编写这样一个对象。具体地说,我已经意识到,在执行联锁操作后返回
*this
,允许另一个线程在返回变量之前更改变量。这将使类型的点为空。有可能写这样的东西吗?大概std::atomic解决了这个问题,但我在我的编译器中没有访问权限…

如果没有
std::atomic
,可以使用
boost::atomic
(最新版本),这是经过良好测试的跨平台实现。

如果没有
std::atomic
,您可以使用
boost::atomic
(最新版本),这是经过良好测试的跨平台实现。

运算符
+
-
没有意义。您实际实现的更像是复合赋值(
+=
-=
),但您需要返回类型为
T
的值,而不是对
(*此)
的引用。当然,这不符合赋值运算符的约定
std::atomic
选择使用命名函数,而不是运算符重载,除了
++
--
之外,可能是因为这个原因。

运算符
+
-
没有意义。您实际实现的更像是复合赋值(
+=
-=
),但您需要返回类型为
T
的值,而不是对
(*此)
的引用。当然,这不符合赋值运算符的约定
std::atomic
选择使用命名函数,而不是运算符重载,除了
++
--
,可能是因为这个原因。

除了nogard的“使用其他人已经测试和工作过的实现”这一非常好的建议之外,我建议您不要返回
*这个
,但是操作的结果——这就是现有互锁操作符的工作方式(以及std::atomic的工作方式)

换句话说,您的操作员代码应该如下所示:

T Interlocked<T, sizeof(__int32)>::operator+(T val)
{
    return InterlockedExchangeAdd((LONG volatile *)&m_val, (LONG) val);
}
实际上可以做到:

b += c; 
a = b;

除了nogard提出的“使用其他人已经测试和工作的实现”的非常好的建议外,我建议您不要返回
*this
,而是操作的结果-这是现有互锁操作符的工作方式(以及std::atomic的工作方式)

换句话说,您的操作员代码应该如下所示:

T Interlocked<T, sizeof(__int32)>::operator+(T val)
{
    return InterlockedExchangeAdd((LONG volatile *)&m_val, (LONG) val);
}
实际上可以做到:

b += c; 
a = b;

考虑两个线程在原子序数类上同时执行加法,其中线程#n将一个数量
t\u n
添加到您的数字
x

您担心在一个线程中执行加法和返回结果之间,第二个线程可能会执行加法,从而弄乱第一个线程的返回值

该类用户的观察行为是返回值为
(x+t\u 1+t\u 2)
,而不是预期的
(x+t\u 1)

现在让我们假设您有一个不允许该行为的实现,即结果保证为
(x#u 1+t_1)
,其中
x_1
是线程1执行加法之前的数值

如果线程#2在线程#1之前立即执行并发加法,则得到的值为:

(x_1 + t_1) = ((x + t_2) + t_1)

这是完全相同的种族。除非在应用加法之前引入一些额外的同步或检查数字的预期值,否则您将始终获得此竞赛。

考虑两个线程在原子数类上执行并发加法,其中线程#n将一个量
t#n
添加到您的数字
x

您担心在一个线程中执行加法和返回结果之间,第二个线程可能会执行加法,从而弄乱第一个线程的返回值

该类用户的观察行为是返回值为
(x+t\u 1+t\u 2)
,而不是预期的
(x+t\u 1)

现在让我们假设您有一个不允许该行为的实现,即结果保证为
(x#u 1+t_1)
,其中
x_1
是线程1执行加法之前的数值

如果线程#2在