小编Sil*_*ler的帖子

std :: call_once vs std :: mutex用于线程安全初始化

我对目的有点困惑std::call_once.需要明确的是,我明白了什么std::call_once ,以及如何使用它.它通常用于原子初始化某个状态,并确保只有一个线程初始化状态.我也在网上看过很多尝试用它来创建一个线程安全的单例std::call_once.

如此处所示,假设您编写了一个线程安全的单例,如下所示:

CSingleton& CSingleton::GetInstance()
{
    std::call_once(m_onceFlag, [] {
        m_instance.reset(new CSingleton);
    });
    return *m_instance.get();
}
Run Code Online (Sandbox Code Playgroud)

好的,我明白了.但我认为唯一std::call_once真正保证的是传递的函数只会被执行一次.但它是否保证如果在多个线程之间有一个竞争调用该函数,并且一个线程获胜,其他线程将阻塞,直到获胜线程从该调用返回?

因为如果是这样,我认为call_once普通同步互斥锁没有区别,例如:

CSingleton& CSingleton::GetInstance()
{
    std::unique_lock<std::mutex> lock(m_mutex);
    if (!m_instance)
    {
      m_instance.reset(new CSingleton);
    }
    lock.unlock();

    return *m_instance;
}
Run Code Online (Sandbox Code Playgroud)

那么,如果std::call_once确实迫使其他线程阻塞,那么std::call_once普通互斥锁会带来哪些好处呢?再考虑一下,std::call_once肯定强制阻止其他线程,或者在用户提供的函数中完成的任何计算都不会同步.那么,std::call_once在普通互斥体之上提供什么呢?

c++ multithreading c++11

15
推荐指数
1
解决办法
7145
查看次数

std :: atomic_flag作为成员变量

std::atomic_flag在类构造函数中初始化一个安全的方法是什么?

这个问题好像在问我要问的同一个问题 - 除了这里提问者抱怨编译问题.

我的问题涉及C++标准本身.根据此站点,std::atomic_flag未指定初始化using构造函数初始化程序语法.

std::atomic_flag static_flag = ATOMIC_FLAG_INIT; // static initialization,
// guaranteed to be available during dynamic initialization of static objects.

int main()
{
    std::atomic_flag automatic_flag = ATOMIC_FLAG_INIT; // guaranteed to work
//    std::atomic_flag another_flag(ATOMIC_FLAG_INIT); // unspecified
}
Run Code Online (Sandbox Code Playgroud)

这些信息是否正确?如果是这样,我认为:

struct Foo
{
  Foo() : flag(ATOMIC_FLAG_INIT)
  { }

  std::atomic_flag flag;
};
Run Code Online (Sandbox Code Playgroud)

......也没有说明.那么,这是否意味着我们不能使用std::atomic_flag作为类的成员变量?或者如果我们std::atomic_flag::clear()从类构造函数中简单调用它是否安全?

c++ c++11

14
推荐指数
1
解决办法
2541
查看次数

CPython - 将GIL锁定在主线程中

CPython线程支持的文档是令人沮丧的矛盾和稀疏.

通常,似乎每个人都同意嵌入Python的多线程C应用程序必须始终在调用Python解释器之前获取GIL.通常,这通过以下方式完成:

PyGILState_STATE s = PyGILState_Ensure();

/* do stuff with Python */

PyGILState_Release(s);
Run Code Online (Sandbox Code Playgroud)

这些文档很明显地说明了这一点:https://docs.python.org/2/c-api/init.html#non-python-created-threads

但是,在实践中,获得一个嵌入Python实际工作顺利的多线程C程序是另一回事.即使您完全遵循文档,似乎也有很多怪癖和惊喜.

例如,似乎在幕后,Python区分"主线程"(我猜是调用的线程Py_Initialize)和其他线程.具体来说,任何尝试获取GIL并在"主"线程中运行Python代码的尝试一直都失败了 - 当我尝试这样做时 - (至少使用Python 3.x),程序会中止一条Fatal Python error: drop_gil: GIL is not locked消息,这很愚蠢,因为当然GIL被锁定了!

例:

int main()
{
    Py_Initialize();
    PyEval_InitThreads();
    PyEval_ReleaseLock();

    assert(PyEval_ThreadsInitialized());

    PyGILState_STATE s = PyGILState_Ensure();

    const char* command = "x = 5\nfor i in range(0,10): print(x*i)";
    PyRun_SimpleString(command);

    PyGILState_Release(s);
    Py_Finalize();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个简单的程序以"GIL未锁定错误"中止,即使我明确锁定它.但是,如果我生成另一个线程,并尝试在该线程中获取GIL,一切正常.

所以CPython似乎有一个(未记录的)"主线程"概念,它与C产生的辅助线程有某种不同.

问题:这是否记录在任何地方?有没有人有任何经验可以说明获取GIL的具体规则是什么,如果处于"主要"线程而不是子线程应该对此有任何影响?

PS:另外,我已经注意到这PyEval_ReleaseLock是一个弃用的API调用,但我还没有看到任何实际可行的替代方案.如果您在通话PyEval_ReleaseLock后没有打电话PyEval_InitThreads,您的程序会立即挂起.然而,在该文档中提到的新的替代方案,PyEval_SaveThread …

c python cpython

14
推荐指数
1
解决办法
1067
查看次数

CPU Relax指令和C++ 11原语

我注意到许多使用特定于操作系统的原语实现的无锁算法,例如此处描述的自旋锁(使用特定于Linux的原子原语)通常会使用"cpu relax"指令.通过GCC,可以通过以下方式实现:

asm volatile("pause\n": : :"memory");
Run Code Online (Sandbox Code Playgroud)

具体来说,该指令通常用于while循环自旋锁体中,同时等待变量设置为某个值.

C++ 11似乎没有提供任何类型的便携式"cpu_relax"类型指令.这有什么理由吗?"暂停"声明实际上是否实现了有用的功能?

编辑:

另外,我会问:为什么C++ 11标准委员会决定不包括通用std::cpu_relax()或其他什么?保证便携性是否太难了?

c++ multithreading gcc atomic c++11

14
推荐指数
1
解决办法
5746
查看次数

比较和交换:通过不同的数据大小进行同步

使用GCC内置C原子基元,我们可以使用执行原子CAS操作__atomic_compare_exchange.

与C++ 11的std::atomic类型不同,GCC C原子基元在常规非原子整数类型上运行,包括cmpxchg16b支持的平台上的128位整数.(C++标准的未来版本可能支持std::atomic_view类模板的类似功能.)

这让我有疑问:

如果对较大数据大小的原子CAS操作观察到由同一内存位置上的原子操作发生的更改,但使用较小的数据大小,会发生什么?

例如,假设我们有:

struct uint128_type {
  uint64_t x;
  uint64_t y;
} __attribute__ ((aligned (16)));
Run Code Online (Sandbox Code Playgroud)

假设我们有一个类型的共享变量uint128_type,例如:

uint128_type Foo;
Run Code Online (Sandbox Code Playgroud)

现在,假设线程A执行:

    Foo expected = { 0, 0 };
    Foo desired = { 100, 100 };
    int result = __atomic_compare_exchange(
        &Foo, 
        &expected, 
        &desired, 
        0, 
        __ATOMIC_SEQ_CST
    );
Run Code Online (Sandbox Code Playgroud)

而线程B做:

    uint64_t expected = 0;
    uint64_t desired = 500;
    int result = __atomic_compare_exchange(
        &Foo.x, 
        &expected, 
        &desired, 
        0, 
        __ATOMIC_SEQ_CST
    );
Run Code Online (Sandbox Code Playgroud)

如果线程A的16字节CAS发生在线程B的8字节CAS之前会发生什么(反之亦然)?CAS是否正常失败?这甚至是定义的行为吗?这是否可能在支持16b CAS的x86_64等典型架构上"做正确的事"?

编辑:要清楚,因为它似乎引起混淆,我不是 …

c++ gcc atomic

14
推荐指数
1
解决办法
753
查看次数

原子变量的对齐

当然,为了使典型的现代处理器体系结构(如x86_64)执行原子加载或存储,需要对齐要读/写的数据.

但是这个要求是如何通过C++ 11 <atomic>变量实际实现/实施的呢?

假设我有一个支持16字节比较和交换(双字CAS)的架构,因此它可以原子地读/写16字节值,并且我定义了一个16字节类型:

struct double_word
{
    std::uint64_t x;
    std::uint64_t y;
};
Run Code Online (Sandbox Code Playgroud)

现在,假设我包含std::atomic<double_word>一个类的成员字段:

class foo
{
    public:

    std::atomic<double_word> dword;
};
Run Code Online (Sandbox Code Playgroud)

我如何知道foo::dword实际上是在16字节边界上对齐?我怎么知道调用dword.load()实际上原子的?

实际上,我最初开始问这个问题是因为我之前添加了另一个数据成员时发生的奇怪事情foo::dword.我定义foo为:

class foo
{
    public:

    std::uint64_t x;
    std::atomic<double_word> dword; 
};
Run Code Online (Sandbox Code Playgroud)

当我foo::dword在运行Debian Linux的x86_64机器上实际执行原子加载,并使用GCC 4.7.2编译和运行时,它实际上给了我一个分段错误!

完整计划:

#include <atomic>
#include <cstdint>

    struct double_word
    {
        std::uint64_t x;
        std::uint64_t y;
    };

    class foo
    {
        public:

        std::uint64_t x;
        std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
    };

    int …
Run Code Online (Sandbox Code Playgroud)

c++ atomic c++11

11
推荐指数
1
解决办法
2233
查看次数

C++ 11有状态分配器是否可以跨类型边界互换?

我的问题基本上是跟进:

考虑到复制构造的要求,如何在C++ 11中编写有状态分配器?

基本上,尽管C++ 11标准现在允许有状态分配器,但我们仍然要求如果复制某个Allocator,则副本必须通过==运算符与原始值进行比较.这表明副本可以安全地释放由原始分配的内存,反之亦然.

因此,这样就可以禁止分配器维护独特的内部状态,例如slab-allocator或内存池等等.一种解决方案是使用shared_ptr指针实现习惯用于内部状态,以便某些原始的所有副本Allocator使用相同的底层内存池.那不算太糟糕.除了...

根据上面提到的问题,以及接受的答案,标准似乎需要Allocator<T>具有可互操作的拷贝构造函数Allocator<U>,因此:

Allocator<T> alloc1;
Allocator<U> alloc2(alloc1);
assert(alloc1 == alloc2); // must hold true
Run Code Online (Sandbox Code Playgroud)

换句话说,无论模板参数如何,分配器类型都必须是可互操作的.这意味着如果我使用分配一些内存Allocator<T>,我必须能够使用Allocator<U>从原始构造的实例释放该内存Allocator<T>.

...这对于任何尝试编写使用某种基于大小的内存池的分配器来说都是一个显示阻塞,就像simple_segregated_storage池只返回基于某个大小的块一样sizeof(T).

但是......这是真的吗?

我意识到需要可互操作的复制构造函数,Allocator<T>::rebind因此容器的用户不需要知道say的内部细节,链接列表节点类型等.但据我所看到的,标准本身似乎并没有说什么,以便严厉作为一个要求Allocator<U>构建从Allocator<T>一定原文比较平等Allocator<T>的实例.

该标准基本上需要以下语义,其中X是类型Allocator<T>,a1a2X的实例,Y是类型Allocator<U>,b是实例Allocator<U>.

来自: …

c++ allocator c++11

11
推荐指数
1
解决办法
889
查看次数

用于在没有分配器的Container中服从propagate_on_copy_assignment的习惯用法

假设您有一个Container,它在内部使用其他标准容器来形成更复杂的数据结构.值得庆幸的是,标准容器已经设计用于执行所有必要的工作以确保分配器被复制/分配等.

所以,通常如果我们有一些Container c,并且在内部它有一个std::vector<int>,我们可以编写一个复制赋值运算符,它只是说:

Container& operator = (const Container& c) { m_vec = c.m_vec; return *this; }
Run Code Online (Sandbox Code Playgroud)

实际上我们甚至不必编写它(因为它只是默认的复制赋值运算符所做的),但是我们只是说在这种情况下,默认运算符不会执行一些额外的必需逻辑:

Container& operator = (const Container& c) 
{
   /* some other stuff... */
   m_vec = c.m_vec;
   return *this; 
}
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下没有问题,因为向量赋值运算符为我们完成了所有工作,以确保分配器正确地复制或不复制.

但是......如果我们有一个我们不能简单地复制分配的矢量怎么办?假设它是指向其他内部结构的指针的向量.

假设我们有一个包含指针的内部向量: std::vector<node*, Alloc>

所以,通常在我们的复制赋值运算符中,我们必须说:

Container& operator = (const Container& other) 
{
   vector<node*, Alloc>::allocator_type alloc = m_vec.get_allocator();
   for (auto it = m_vec.begin(); it != m_vec.end(); ++it) alloc.deallocate(*it);
   m_vec.clear();

   for (auto it = other.m_vec.begin(); it != other.m_vec.end(); ++it)
   {
     node* n = …
Run Code Online (Sandbox Code Playgroud)

c++ vector allocator c++11

11
推荐指数
2
解决办法
427
查看次数

SVG堆叠会导致失真

我正在尝试使用SVG堆叠技术来将多个图标堆叠在一个文件中,只需要一个来自浏览器的HTTP请求.是相当彻底描述的技术在这里.

基本上,您的想法是将多个SVG元素放入单个SVG文件中,并使用CSS样式隐藏所有图标,但您当前要显示的图标除外.您可以使用CSS :target选择器选择当前要显示的图标.

该技术适用于我,除非堆叠多个图标会在显示的图标中引起奇怪的扭曲,即使所有其他图标都被隐藏.

在我正在使用的示例中,我将其简化为仅堆叠两个图标:美国国旗图标和英国国旗图标.

(简化的)SVG文件是:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<svg id="svg153" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="640" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">

    <svg:style 
        xmlns:svg="http://www.w3.org/2000/svg" type="text/css">
        .i { display: none; }
        .i:target { display: block; }
    </svg:style>

    <svg:svg id="uk" xmlns:svg="http://www.w3.org/2000/svg" class = "i" height="480" width="640" version="1.1">
       <!-- SVG elements to draw UK flag -->
    </svg:svg>

    <svg:svg id="us" xmlns:svg="http://www.w3.org/2000/svg" class = "i" height="480" width="640" version="1.1">
       <!-- SVG elements to draw US flag --> 
    </svg:svg>
</svg>
Run Code Online (Sandbox Code Playgroud)

请注意,CSS嵌入在<svg::style>元素中的SVG文件中.CSS很简单: …

html css svg

11
推荐指数
1
解决办法
700
查看次数

了解用于剪切树状图的DynamicTreeCut算法

树形图是与层次聚类算法一起使用的数据结构,其在树的不同"高度"处对聚类进行分组 - 其中高度对应于聚类之间的距离度量.

在从某些输入数据集创建树形图之后,通常还需要确定"切割"树状图的位置,这意味着选择高度使得仅低于该高度的聚类被认为是有意义的.

在剪切树状图的高度并不总是很清楚,但是存在一些算法,例如DynamicTreeCut算法,它试图以编程方式从树形图中选择有意义的聚类.

看到:

https://stats.stackexchange.com/questions/3685/where-to-cut-a-dendrogram

在最高纯度水平切割树状图

所以我一直在阅读DynamicTreeCut算法,以及该算法的Java实现.从逐步分解正在发生的事情的角度来看,我理解算法是如何工作的以及它在做什么.但是,我无法理解这个算法是如何做任何有意义的事情的.我想我在这里缺少一些关键概念.

通常,该算法在树形图上迭代"高度序列".我不确定,但我认为"高度序列"只是指树状图沿Y轴的值,即簇连接发生的各种高度.如果是这种情况,我们可以假设"高度序列"按升序排序.

然后算法要求获取"参考高度",l并从输入"高度序列"中的每个高度减去它.这为您提供了高度序列中D每个高度h[i]与参考高度之间差异()的向量l.

然后算法尝试找到"转换点" - 它们是差异向量中的点,其中D[i] > 0D[i+1] < 0.换句话说,差异向量中的点,其中差值从正变为负.

就在这里,我完全迷失了.我不明白这些过渡点是如何有意义的.首先,我的理解是输入高度序列H只是树形图Y轴上的值.因此,高度序列H应按升序排列.因此,如何在差异向量中找到一个从正向过渡到负向的点

例如:

假设我们的输入高度序列H是{1, 1.5, 2, 2.5, 3, 7, 9},我们的参考值l是平均高度(将是3.7).因此,如果我们D通过l从每个高度中减去来创建差异向量H,我们就会得到{-2.7, -2.2, -1.7, -1.2, -0.7, 3.3, 5.3}.很明显,这里没有过渡点,也没有过,因为差异向量中没有点D[i] …

algorithm cluster-analysis hierarchical-clustering dendrogram unsupervised-learning

11
推荐指数
1
解决办法
763
查看次数