相关疑难解决方法(0)

ptr为NULL的free(ptr)是否会损坏内存?

从理论上讲,我可以这么说

free(ptr);
free(ptr); 
Run Code Online (Sandbox Code Playgroud)

是一个内存损坏,因为我们释放已经释放的内存.

但是如果

free(ptr);
ptr=NULL;
free(ptr); 
Run Code Online (Sandbox Code Playgroud)

由于操作系统将以不确定的方式运行,我无法对此进行实际的理论分析.无论我在做什么,这种记忆是否会腐败?

释放空指针有效吗?

c free null pointers

106
推荐指数
7
解决办法
7万
查看次数

此信封实现是否正确使用C ++ 11原子?

我编写了一个简单的“信封”类,以确保我正确理解C ++ 11原子语义。我有一个标头和一个有效负载,编写器清除该标头,填充有效负载,然后用递增的整数填充标头。这样的想法是,读取器然后可以读取标头,将有效负载换出,再次读取标头,如果标头相同,则读取器可以假定他们成功复制了有效负载。读者可能会错过一些更新是可以的,但是让他们获得更新的撕裂(其中来自不同更新的字节混合在一起)也不是可以的。永远只有一个读者和一个作家。

编写者使用释放内存顺序,而读者使用获取内存顺序。

是否存在通过原子存储/加载调用对memcpy重新排序的风险?还是可以将负载彼此重新排序?这永远不会让我流产,但也许我很幸运。

#include <iostream>
#include <atomic>
#include <thread>
#include <cstring>

struct envelope {
    alignas(64) uint64_t writer_sequence_number = 1;
    std::atomic<uint64_t> sequence_number;
    char payload[5000];

    void start_writing()
    {
        sequence_number.store(0, std::memory_order::memory_order_release);
    }

    void publish()
    {
        sequence_number.store(++writer_sequence_number, std::memory_order::memory_order_release);
    }

    bool try_copy(char* copy)
    {
        auto before = sequence_number.load(std::memory_order::memory_order_acquire);
        if(!before) {
            return false;
        }
        ::memcpy(copy, payload, 5000);
        auto after = sequence_number.load(std::memory_order::memory_order_acquire);
        return before == after;
    }
};

envelope g_envelope;

void reader_thread()
{
    char local_copy[5000];
    unsigned messages_received = 0;
    while(true) {
        if(g_envelope.try_copy(local_copy)) {
            for(int i …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading memory-model thread-safety stdatomic

7
推荐指数
1
解决办法
154
查看次数

编译器屏障的目的是什么?

以下摘自Concurrent Programming on windows,第10章第528~529页,一个c++模板Double check实现

T getValue(){
    if (!m_pValue){
        EnterCriticalSection(&m_crst);
        if (! m_pValue){
            T pValue = m_pFactory();
            _WriteBarrier();
            m_pValue = pValue;                  
        }
        LeaveCriticalSection(&m_crst);
    }
      _ReadBarrier();
  return m_pValue;
}
Run Code Online (Sandbox Code Playgroud)

正如作者所说:

在实例化对象之后,但在 m_pValue 字段中写入指向它的指针之前,会找到 _WriteBarrier。这是确保对象初始化中的写入永远不会延迟到对 m_pValue 本身的写入之后所必需的。

由于_WriteBarrier 是编译屏障,如果编译知道LeaveCriticalSection 的语义,我认为它没有用。编译可能会省略对 pValue 的写入,但永远不会优化在函数调用之前移动赋值,否则会违反程序语义。我相信 LeaveCriticalSection 具有隐式硬件围栏。因此,在分配给 m_pValue 之前的任何写入都将被同步。

另一方面,如果编译不知道 LeaveCriticalSection 的语义,则所有平台都需要 _WriteBarrier以防止编译将赋值移出临界区。

而对于_ReadBarrier,作者说

类似地,我们在返回 m_value 之前需要一个 _ReadBarrier,以便调用 getValue 之后的加载不会重新排序在调用之前发生。

首先,如果这个函数包含在一个库中,并且没有可用的源代码,那么编译如何知道是否存在编译障碍?

其次,如果需要,它会被放置在错误的位置,我认为我们需要将它放在 EnterCriticalSection 之后以表达获取栅栏。与我上面写的类似,这取决于编译是否理解 EnterCriticalSection 的语义。

而且作者还说:

但是,我还要指出,X86、Intel64 和 AMD64 处理器都不需要栅栏。不幸的是,像 IA64 这样的弱处理器已经把水搅浑了

正如我上面分析的,如果我们在某个平台上需要那些屏障,那么我们在所有平台上都需要它们,因为那些屏障是编译屏障,它只是确保编译可以做正确的优化,以防万一他们不明白一些函数的语义。

如果我错了,请纠正我。

另一个问题,msvc 和 gcc 有什么参考可以指出他们理解同步语义的函数吗? …

c++ concurrency multithreading atomic memory-barriers

6
推荐指数
1
解决办法
4029
查看次数