C/C++ 编译器可以通过 pthread 库调用合法地将变量缓存在寄存器中吗?

2023-11-25

假设我们有以下代码:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void guarantee(bool cond, const char *msg) {
    if (!cond) {
        fprintf(stderr, "%s", msg);
        exit(1);
    }
}

bool do_shutdown = false;   // Not volatile!
pthread_cond_t shutdown_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t shutdown_cond_mutex = PTHREAD_MUTEX_INITIALIZER;

/* Called in Thread 1. Intended behavior is to block until
trigger_shutdown() is called. */
void wait_for_shutdown_signal() {

    int res;

    res = pthread_mutex_lock(&shutdown_cond_mutex);
    guarantee(res == 0, "Could not lock shutdown cond mutex");

    while (!do_shutdown) {   // while loop guards against spurious wakeups
        res = pthread_cond_wait(&shutdown_cond, &shutdown_cond_mutex);
        guarantee(res == 0, "Could not wait for shutdown cond");
    }

    res = pthread_mutex_unlock(&shutdown_cond_mutex);
    guarantee(res == 0, "Could not unlock shutdown cond mutex");
}

/* Called in Thread 2. */
void trigger_shutdown() {

    int res;

    res = pthread_mutex_lock(&shutdown_cond_mutex);
    guarantee(res == 0, "Could not lock shutdown cond mutex");

    do_shutdown = true;

    res = pthread_cond_signal(&shutdown_cond);
    guarantee(res == 0, "Could not signal shutdown cond");

    res = pthread_mutex_unlock(&shutdown_cond_mutex);
    guarantee(res == 0, "Could not unlock shutdown cond mutex");
}

符合标准的 C/C++ 编译器是否可以缓存以下值do_shutdown在寄存器中通过调用pthread_cond_wait()?如果不是,哪些标准/条款可以保证这一点?

编译器可以假设知道pthread_cond_wait()不修改do_shutdown。这看起来不太可能,但据我所知,没有任何标准可以阻止这种情况发生。

实际上,任何 C/C++ 编译器都会缓存以下值do_shutdown在寄存器中通过调用pthread_cond_wait()?

编译器保证不缓存哪个函数调用的值do_shutdown穿过?很明显,如果函数是在外部声明的并且编译器无法访问其定义,则它不能对其行为做出任何假设,因此无法证明它没有访问do_shutdown。如果编译器可以内联该函数并证明它没有访问do_shutdown,那么可以缓存吗do_shutdown即使在多线程设置中?同一编译单元中的非内联函数怎么样?


当然,当前的 C 和 C++ 标准没有提及这个主题。

据我所知,Posix 仍然避免正式定义并发模型(不过,我可能已经过时了,在这种情况下,我的答案仅适用于早期的 Posix 版本)。因此,必须带着一点同情心来阅读它所说的内容 - 它没有精确地列出该领域的要求,但实现者应该“知道它的含义”并做一些使线程可用的事情。

当标准说互斥体“同步内存访问”时,实现必须假设这意味着在一个线程的锁下所做的更改将在其他线程的锁下可见。换句话说,同步操作包括一种或另一种内存屏障是必要的(尽管还不够),并且内存屏障的必要行为是它必须假设全局变量可以更改。

线程不能作为库实现涵盖了 pthread 实际可用所需的一些特定问题,但在撰写本文时(2004 年)Posix 标准中尚未明确说明。在允许程序员“令人信服地推理程序正确性”方面,您的编译器编写者或为您的实现定义内存模型的任何人是否同意 Boehm 的“可用”含义变得非常重要。

请注意,Posix 不保证一致的内存缓存,因此如果您的实现反常想要缓存do_something在你的代码的寄存器中,然后即使您将其标记为易失性,它可能会反常地选择在同步操作和读取之间不弄脏 CPU 的本地缓存do_something。因此,如果编写器线程在具有自己的缓存的不同 CPU 上运行,即使这样您也可能看不到更改。

这就是线程不能仅作为库实现的(原因之一)。这种仅从本地 CPU 缓存获取易失性全局变量的优化在单线程 C 实现中有效[*],但会破坏多线程代码。因此,编译器需要“了解”线程,以及它们如何影响其他语言功能(例如 pthreads 之外的示例:在 Windows 上,缓存始终是一致的,Microsoft 阐明了它授予的附加语义volatile在多线程代码中)。基本上,您必须假设,如果您的实现在提供 pthreads 函数方面遇到了麻烦,那么它也会在定义一个可行的内存模型(其中锁实际上同步内存访问)方面遇到麻烦。

如果编译器可以内联 函数并证明它不访问 do_shutdown,那么是否可以缓存 do_shutdown 即使在多线程中 环境?非内联的怎么样 在同一个编译单元中运行?

对所有这些都是肯定的 - 如果该对象是非易失性的,并且编译器可以证明该线程不会修改它(通过其名称或通过别名指针),并且如果没有发生内存障碍,那么它可以重用以前的值。当然,有时可能还会存在其他特定于实现的条件来阻止它。

[*] 前提是实现知道全局不位于某个“特殊”硬件地址,这要求读取始终通过缓存到达主内存,以便查看影响该地址的任何硬件操作的结果。但是要将全局变量放在任何此类位置,或者通过 DMA 或其他方式使其位置特殊,需要特定于实现的魔法。如果没有任何这样的魔法,原则上的实现有时可以知道这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++ 编译器可以通过 pthread 库调用合法地将变量缓存在寄存器中吗? 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • ASP.NET Core Serilog 未将属性推送到其自定义列

    我有这个设置appsettings json对于我的 Serilog 安装 Serilog MinimumLevel Information Enrich LogUserName Override Microsoft Critical Wr
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框

随机推荐

  • Android 通知未显示超过 3 个操作

    我想显示包含 5 个操作的通知 但它只显示其中 3 个 这是我用来显示它的代码 Notification notification new android support v7 app NotificationCompat Builder
  • 如何在 PHP 中代理另一个页面

    我正在寻找最快 最简单的方法来代理 PHP 页面 我不希望用户被重定向 我只希望我的脚本返回与另一个远程 URL 相同的内容 响应代码和标头 echo file get contents proxypage 那行得通吗 EDIT 第一个答案
  • 如何获取

    如标题 副标题 歌手 专辑 比特率等 wiki MP3 标签信息 wiki ID3 mp3 元数据格式 我搜索了很多 但我无法得到答案 只搜索了如何播放 停止 重新加载音频 浏览器不支持 又一个图书馆位于https github com a
  • 如何更改单个提交的提交作者?

    我想更改历史中某个特定提交的作者 这不是最新的提交 Related 如何更改多次提交的作者和提交者姓名 电子邮件 从历史记录中比您需要修改的提交更早的点进行交互式变基 git rebase i
  • JavaScript 中的不可变哈希和数组实现?

    有没有简单的不可变的javascript 中的哈希和数组实现 我不需要最好的速度 比克隆更好的合理速度就很好了 另外 如果有simpleJava 或其他一些语言的实现可以是易于理解和移植对于JavaScript来说 这也很好 UPDATE
  • 为什么 Azure AD 在多租户场景中无法登录非管理员?

    环境 两个 Azure AD Company 顾客 Company发布一个名为的 ASP NET5 Web 应用程序Portal 该应用程序设置为多租户 顾客有 2 个用户 user 谁只是一个用户 和admin 谁是目录中的全局管理员 P
  • 如何使用 django-smart-select

    假设我有以下模型 class Location models Model continent models CharField max length 20 country models ForeignKey Country 我需要创建一个依
  • Javascript nodeValue 返回 null

    标题应该很好地描述我的问题 这是我的代码 div div
  • JSONP的解释和使用[重复]

    这个问题在这里已经有答案了 可能的重复 请解释一下 JSONP 什么是 JSONP 为什么需要它 以及它的一些实际用法示例 JSONP 代表带有填充的 JSON 它为客户端提供了一种方法来指定应添加到 JSON 响应开头的一些代码 这允许
  • 通过曲面细分着色器将数据传递到片段着色器

    我对着色器管道在每个阶段传递数据方面的工作原理感到有点困惑 我想做的是传递在顶点阶段加载的颜色数据glVertexAttrib4fv 通过曲面细分控制着色器 然后是曲面细分评估着色器 这样就可以在片段着色器中使用 我不确定我是否犯了某种概念
  • 使用python输出到两个不同的控制台

    我正在编写一个脚本 并且有两种不同类型的输出 例如 Op1 和 Op2 我想将 Op1 输出到调用 python 进程的终端 而 Op2 应该转储到不同的终端实例 我可以这样做吗 即使答案是特定于 Linux 的也没关系 我需要一个临时解决
  • 为什么 document.getElementsBy__ 方法不返回 HTMLCollection?

    为什么不getElementsByName getElementsByTagName and getElementsByClassName返回一个HTMLCollection W3C MDN 代替NodeList W3C MDN 所有这三个
  • 什么是 org.eclipse.wst.common.component 以及如何将它用于 ant

    我有一个 Eclipse 工作区 用于工作 其中有一个 Ear 项目 我用它来 导出 将 Ear 文件部署到 JBoss 服务器 然而 他们最近要求我创建一个可以构建工作区并创建耳朵的 Ant 脚本 我已经成功地完成了这一点 当我尝试使用使
  • 带有Rails ajax的pushState

    我有一个索引操作页面 显示了用 Kaminari 分页的项目列表 我已经向它们添加了 ajax 功能 现在正在尝试使用 PushState 来获取适合的 URL 我的问题是 当我的分页链接通过以下方式完成时 如何获取传递给 PushStat
  • Gson 可选字段和必填字段

    一个人应该如何应对Gson必填字段与可选字段 由于所有字段都是可选的 因此我无法根据响应 json 是否包含某个键来真正使我的网络请求失败 Gson将简单地将其解析为 null 我正在使用的方法gson fromJson json mCla
  • 增加具有不同线程数的 2 个交替 OpenMP 并行区域的内存消耗

    当我重复在具有不同线程数的 2 个 OpenMP 并行区域之间交替时 内存消耗无限增加 数十 MB 甚至更多 即使在如此简单的代码片段中它也能做到这一点 include
  • 轮盘赌选择算法[重复]

    这个问题在这里已经有答案了 谁能提供一些轮盘赌选择函数的伪代码 我将如何实现这个 我真的不明白如何阅读这个数学符号 我想要通用算法 其他答案似乎假设您正在尝试实现轮盘赌游戏 我认为您问的是进化算法中的轮盘赌选择 这是一些Java代码实现轮盘
  • 如何在 Elixir 中重写 Erlang 组合算法?

    过去几周我一直在修补 Elixir 我刚刚遇到这个简洁的Erlang 中的组合算法 我尝试在 Elixir 中重写但卡住了 艾尔兰版本 comb 0 gt comb gt comb N H T gt H L L lt comb N 1 T
  • 在 Android 应用程序中显示日历

    如果有人能为我提供一个关于使用哪个组件的解决方案 以便我可以在我的 Android 应用程序中显示一个相当漂亮的日历 我将不胜感激 这两天我一直在寻找解决方案 但不幸的是找不到解决方案 由于这是一个非常基本的要求 我相信会有一个完美的解决方
  • C/C++ 编译器可以通过 pthread 库调用合法地将变量缓存在寄存器中吗?

    假设我们有以下代码 include