首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >静态初始化是否对所有对象都是原子的?

静态初始化是否对所有对象都是原子的?
EN

Stack Overflow用户
提问于 2022-05-15 02:23:44
回答 2查看 180关注 0票数 1

C++11保证在函数的第一次调用时静态局部变量的初始化是原子的。尽管该标准没有强制执行任何实现,但有效处理此问题的唯一方法是双重检查锁定。

我问自己,是否所有对象都初始化了,是否在同一个互斥对象之间初始化(可能),或者每个静态对象初始化是否对自己的互斥对象起作用(不太可能)。因此,我编写了这个litlte ++20-程序,它使用一些可变的和折叠的表达式技巧来拥有许多不同的函数,每个函数都初始化自己的静态对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <utility>
#include <latch>
#include <atomic>
#include <chrono>
#include <thread>

using namespace std;
using namespace chrono;

atomic_uint globalAtomic;

struct non_trivial_t
{
    non_trivial_t() { ::globalAtomic = ~::globalAtomic; }
    non_trivial_t( non_trivial_t const & ) {}
    ~non_trivial_t() { ::globalAtomic = ~::globalAtomic; }
};

int main()
{
    auto createNThreads = []<size_t ... Indices>( index_sequence<Indices ...> ) -> double
    {
        constexpr size_t N = sizeof ...(Indices);
        latch latRun( N );
        atomic_uint synch( N );
        atomic_int64_t nsSum( 0 );
        auto theThread = [&]<size_t I>( integral_constant<size_t, I> )
        {
            latRun.arrive_and_wait();
            if( synch.fetch_sub( 1, memory_order_relaxed ) > 1 )
                while( synch.load( memory_order_relaxed ) );
            auto start = high_resolution_clock::now();
            static non_trivial_t nonTrivial;
            nsSum.fetch_add( duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count(), memory_order_relaxed );
        };
        (jthread( theThread, integral_constant<size_t, Indices>() ), ...);
        return (double)nsSum / N;
    };
    constexpr unsigned N_THREADS = 64;
    cout << createNThreads( make_index_sequence<N_THREADS>() ) << endl;
}

我用上面的代码创建了64个线程,因为我的系统在处理器组中有多达64个CPU(RyzenThreadrapper3990X,Windows 11)。这些结果满足了我的期望,每一次初始化都要花费大约7000 is。如果每个初始化都对它自己的互斥锁起作用,则互斥锁将采用较短的路径,并且没有内核争用,并且时间会更低。还有什么问题吗?

之后我问自己的问题是:如果静态对象的构造函数有自己的静态对象,会发生什么?标准是否明确要求这应该工作,从而迫使实现考虑互斥必须是递归的?

EN

回答 2

Stack Overflow用户

发布于 2022-05-15 03:15:00

不,静态初始化并不是所有对象的原子化。不同的静态对象可以由不同的线程同时初始化。

GCC和Clang实际上确实使用了一个全局递归互斥(用于处理您描述的递归情况,这是工作所必需的),但其他编译器对每个静态函数(本地对象(即Apple的编译器)使用互斥)。因此,您不能依赖一次一个对象的静态初始化--仅仅是因为它没有,这取决于编译器(以及编译器的版本)。

标准的6.7.4部分:

具有静态存储持续时间的POD类型(basic.types)的本地对象在第一次输入其块之前被初始化。实现允许在名称空间范围(basic.start.init)中静态初始化具有静态存储持续时间的对象的相同条件下,对具有静态存储持续时间的其他本地对象执行早期初始化。 否则,这样的对象将在第一次通过其声明时被初始化;这样的对象在初始化完成后被认为是初始化的。如果通过抛出异常退出初始化,则初始化不完成,因此下次控件输入声明时将再次尝试初始化。如果控件在初始化对象时(递归地)重新输入声明,则行为未定义.

标准只禁止对同一个静态对象进行递归初始化;它不禁止对一个静态对象进行初始化以要求对另一个静态对象进行初始化。由于标准明确规定,在第一次执行包含这些对象的块时,必须初始化不属于此禁用类别的所有静态对象,因此允许使用您询问的情况。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int getInt1();
int getInt2() { //This could be a constructor, too, and nothing would change
  static int result = getInt1();
  return result;
}
int getInt3() {
  static int result = getInt2(); //Allowed!
  return result;
}

这也适用于函数的构造函数本地静态对象本身包含这样一个静态对象的情况。构造函数实际上也只是一个函数,这意味着这种情况与上面的示例完全相同。

另见:https://manishearth.github.io/blog/2015/06/26/adventures-in-systems-programming-c-plus-plus-local-statics/

票数 3
EN

Stack Overflow用户

发布于 2022-05-15 06:23:18

每个静态局部变量都必须是原子的。如果它们中的每一个都有自己的互斥锁或双重检查锁,那么这将是正确的。

还可以有一个全局递归互斥体,允许一个线程和一个线程一次只初始化静态局部变量。这也很管用。但是,如果您有许多静态局部变量和多个线程第一次访问它们,那么这可能会非常慢。

但是,让我们考虑静态局部变量具有静态局部变量的情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {
    static int x = foo();
};

void bla() {
    static A a;
};

初始化a需要初始化x。但是,没有什么可以说,不可能有其他线程也有一个A c;,并将初始化x在同一时间。因此,x仍然需要受到保护,即使在bla()的情况下,它是在已经静态的初始化中。

另一个示例(希望编译,尚未检查):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void foo() {
    static auto fn = []() {
        static int x = bla();
    };
}

在这里,x只能在初始化fn时才能初始化。所以编译器可能会跳过保护x。这将是一个最佳化,它遵循“如果是主体”。除了定时之外,x是否受到保护也没有区别。另一方面,x的锁定总是成功的,而且成本非常低。编译器可能不会对其进行优化,因为没有人投入时间来检测和优化这种情况。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72247395

复制
相关文章
查看LINUX发行版的名称及其版本号
查看LINUX发行版的名称及其版本号的命令,这些信息对于添加正确的软件更新源很有用,而当你只能在命令行下工作的时候,下面的方法可以帮忙。
匿名用户的日记
2021/12/14
3.1K0
Linux各发行版本镜像
linux 公众号回复:linux镜像 END
Rookie
2018/07/02
6.4K0
Linux 10个主流发行版本
这里只表示做个记录。其实相关的介绍已经很多了。但是还是想怀缅一下青春。这10个Linux发行版来源于国外网站(最后给出链接)。它列出了10个Linux发行版(包含一个FreeBSD,Linux的胞兄弟),通常被认为是全球Linux用户最广泛使用的。当然并没有经过详细的数字统计,大家仍然可以选择其他的发行版。但是一般来讲,这几个都有非常活跃的论坛或邮件列表,遇到困难,也能比较迅速的解决。
狼啸风云
2019/10/24
18.3K0
查看Linux各发行版本方法
SUSE: cat /etc/SuSE-release Slackware: cat /etc/slackware-version Redhat: cat /etc/redhat-release 上一篇:文件锁flock、lockf和fcntl区别测试程序 下一篇:linux使用vi中文乱码的解决办法
一见
2018/08/07
1.7K0
查看Linux内核版本及发行版本「建议收藏」
访问wiki查看发行版本与内核版本对应关系:https://en.wikipedia.org/wiki/CentOS
全栈程序员站长
2022/11/04
2.3K0
查看Linux内核版本及发行版本「建议收藏」
linux内核版本和发行版本的区别_linux内核版本号的构成
1.内核版本 内核是系统的心脏,是运行程序和管理像磁盘和打印机等硬件设备的核心程序,它提供了一个在裸设备与应用程序间的抽象层。例如,程序本身不需要了解用户的主板芯片集或磁盘控制器的细节就能在高层次上读写磁盘。
全栈程序员站长
2022/09/25
6.1K0
linux内核版本和发行版本的区别_linux内核版本号的构成
2023 | 10个最佳的Linux发行版本
下载链接:https://ubuntu.com/download/desktop/
Linux兵工厂
2023/03/30
3K0
2023 | 10个最佳的Linux发行版本
三十种Linux操作系统发行版名称含义详解
独特的开发模式造就了今天百家争鸣的Linux,各个发行版的名字都代表哪些含义呢?下面是一些常用的版本,感兴趣的朋友一起来看:   1 Ubuntu   这或许是今天最常见的一个Linux发行版了,Ubuntu是一个南非的民族观念,着眼于人们之间的忠诚和联系。该词来自于祖鲁语和科萨语。大意是“人道待人”(对他人仁慈)。另一种翻译是:“天下共享的信念,连接起每个人”。   2 PCLinuxOS   很明显它的名称是PC+Linux+Operating System组合在一起,拥有类似Windows的图形用户界面,方便用户从Windows转向Linux。   3 openSUSE    openSUSE由AMD和Novell共同发起,SUSE是德语“Software-und System-Entwicklung”的缩写(software and system development)。另有一说是为了纪念德国计算机工先驱Konrad Zuse。   4 Fedora   Fedora由Red Hat发起,正是Red Hat标志中人物所戴的那顶帽子。   5 Mandriva    在以前称为Mandrake,并由MandrakeSoft公司进行维护。当MandrakeSoft输掉了与Hearst Corporation争夺Mandrake名称的官司,并收购了Connectiva后,名称变为了Mandriva。   6 Sabayon   一种意大利甜点,也叫Zabaglion,用蛋黄、糖等原料烹饪而成。     7 Debian   这个名字是Ian Murdock在1998年取的,由当初是女朋友现在是妻子的Debra和他的名字混合而来。   8 Damn Small Linux   像它的名字所说那样,这个Linux发行版体积只有50MB。   9 MEPIS   根据创始人Warren Woodford所说,这个名字最初没有任何含义,只是由于朋友在Skype上误听得来的名字。   10 CentOS   CentOS基于Red Had Enterprise Linux(RHEL),代表Community Enterprise Operationg System。   11 Dreamlinux   这是一个巴西的Linux发行版,基于Debian,外观类似苹果的Mac OS X,名字就像介绍中说的那样。   12 Puppy Linux   一种小型的Live CD系统,目标是简单易用,吉祥物是墨西哥小狗吉娃娃。   13 Kubuntu   KDE + Ubuntu的产物   14 Zenwalk   有禅意的名字,不过根据创始人JP Guillemin所说,这个名字是从发音和含义两个方面模仿Neststep操作系统所起。   15 Slackware   创始人是Patrick Volkerding,最初是个人项目,这个名字借用自Church of the SubGenius中的术语 "Slack"。   16 Knoppix   著名的Live CD发行版,基于Dedian,创始人是Klaus Knopper并以此命名。   17 Gentoo   Gentoo是一种体形小巧的企鹅,据说是企鹅中游泳速度最快的一种。   18 Slax   一种基于Slackware的live CD发行版,它的名称并没有特别含义。   19 Sidux   基于Debian尚不稳定,代号“Sid”的一支,名称来自玩具总动员中的角色Sid Phillips。   20 Ubuntu Studio   Ubuntu的派生,以多媒体应用为主。   21 Xubuntu   XFCE + Ubuntu的产物   22 Foresight   Foresight使用Conary作为包管理器,据称,不像大多数发行版一年两次升级那样,它采用滚动升级的方式。   23 Red Hat   创始人之一的Bob Young为您解释其中含义。   24 OpenGEU   OpenGEU最初称为Geubuntu,使用Gnome和Enlightenment作为桌面管理器。   25 Elive   从图标中也可以看出这同样是一款采用Enlightenment作为窗口管理器的发行版,它是基于Debian的Live Cd。   26 Freespire   微软2千万美元买下了“Lindows”这个名字后,Lindows更名为Linspire,Freespire是基于Linspire的源码的Live CD版本。   27 Fluxbuntu   又是Ubuntu的派生版,采用Fluxbox作为窗口管理器。   28 Xandors   X代表X Windows系统,Andros是希腊的岛名。   29 T
苦叶子
2021/10/20
1.1K0
linux内核和发行版有什么区别?附镜像包以及如何查看Linux系统内核版本和发行版本
linux内核和发行版的区别是:linux内核安装完成后没有用户界面和软件,是提供硬件抽象层、硬盘以及文件系统控制的核心程序;而linux发行版是在内核的基础上加入了用户界面和各种软件的支持。
糯米导航
2022/12/10
3.4K0
linux内核和发行版有什么区别?附镜像包以及如何查看Linux系统内核版本和发行版本
Linux - 获取系统版本信息
写 shell 脚本的时候想根据系统版本来做条件判断,所以这篇就是这里搬那里搬,当做记录了
小菠萝测试笔记
2021/11/02
5.3K0
linux shell 获取java版本号
说明: 在shell中要获取java版本号最基本的思路就是从java -version 的输出内容中解析出版本号,但是java -version 的输出并不是输出到默认的stdout,而是stderr,所以要想从java -version 的输出解析出java版本号,就要将java -version 的输出重定向,上面脚本中2>&1就是将原本输出到stderr中的内容重定向输出到stdout,这样就可以将java -version的输出通过管道|输入到sed,awk这些工具去处理了.
10km
2019/08/14
2.6K0
你是否需要一个容器专用的Linux发行版本?
图片来自:lickr/Jonas Smith 单单使用容器是不够的,提供商们认为你需要一个容器专用的Linux发行版本。 我们可以让容器在不同的操作系统上运行,不同的操作系统都有自己的虚拟化服务,如:Solaris Zones、BSD Jails、Linux Docker(Windows现在也支持Docker了)、Linux OpenVZ等等。特别是在Docker和容器技术突然流行开来,操作系统公司正在采取不同的策略。他们认为,大部分容器,应该用瘦操作系统制作。 为什么?(当然,除了给他们一个新的收入来
CSDN技术头条
2018/02/09
1.4K0
你是否需要一个容器专用的Linux发行版本?
根据 PID 获取 K8S Pod名称 - 反之 POD名称 获取 PID
随着 Kubernetes 越来越火爆,运维人员排查问题难度越来越大。比如我们收到监控报警,某台 Kubernetes Node 节点负载高。通过 top 或者 pidstat 命令获取 Pid,问题来了,这个 Pid 对应那个 Kubernetes Pod 呢?
YP小站
2020/07/21
3.4K0
如何修改系统内核版本名称_linux系统查看内核
系统中安装了多个内核版本,一般重启会根据配置文件的启动顺序,选择一个,怎样选择自己想要的版本?
全栈程序员站长
2022/09/25
5.5K0
如何修改系统内核版本名称_linux系统查看内核
hadoop发行版本之间的区别
Hadoop是一个能够对大量数据进行分布式处理的软件框架。 Hadoop 以一种可靠、高效、可伸缩的方式进行数据处理。Hadoop的发行版除了有Apache hadoop外cloudera,hortonworks,mapR,华为,DKhadoop等都提供了自己的商业版本。商业发行版主要是提供了更为专业的技术支持,这对于大型企业更为重要,不同发行版都有自己的一些特点,本文就各发行版做简单对比介绍。
用户3392176
2018/09/18
1.4K0
hadoop发行版本之间的区别
hadoop发行版本之间的区别
Hadoop是一个能够对大量数据进行分布式处理的软件框架。 Hadoop 以一种可靠、高效、可伸缩的方式进行数据处理。Hadoop的发行版除了有Apache hadoop外cloudera,hortonworks,mapR,华为,DKhadoop等都提供了自己的商业版本。商业发行版主要是提供了更为专业的技术支持,这对于大型企业更为重要,不同发行版都有自己的一些特点,本文就各发行版做简单对比介绍。
IT小白龙
2018/09/11
1.3K0
hadoop发行版本之间的区别
Python获取网卡信息(名称、MAC、
    “人生苦短,我用Python”。Python的高效有一部分是跟它丰富的模块分不开的。Python有很多第三方模块可以帮助我们完成一些事情,减少开发时间。
py3study
2020/01/03
4.7K0
Python获取网卡信息(名称、MAC、
更改Linux网卡名称
转载自:https://blog.csdn.net/yeziand01/article/details/88424624
zy010101
2019/07/02
5.1K0
更改Linux网卡名称
最佳 Linux 发行版汇总
Linux入门 Ubuntu Ubuntu是一款基于Debian发行版,以Unity作为默认桌面环境的Linux操作系统。他是世界上最流行的发行版之一,最新发行版为桌面、移动及其桌面移动混合版的优化。
小小科
2018/05/04
6.8K0
最佳 Linux 发行版汇总
点击加载更多

相似问题

如何获取Linux发行版的名称和版本?

82

如何从linux内核代码中获取发行版名称和版本?

15

获取运行linux发行版的名称

17

检查发行版本linux with ip

04

Java: Runtime.exec获取Linux发行版名称

33
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文