C 线程编程 - 增加共享变量

2024-04-03

嘿伙计们...所以我正在尝试温习我的 C 线程,我发现的一个问题是:

给定一个全局变量 int x = 0;实现函数 void 无用(int n),它创建 n 个线程,在循环中将 x 加 1,每个线程在 x 达到 100 时终止。

我只是没有掌握线程,需要一个可靠的例子来奠定我的基础。这就必须尽可能的使用pthread系统调用。


首先,您需要确定您想要实现的目标是什么,以及不同线程的指令可以通过哪些方式交织并防止这种情况发生。

C中的自增运算符++x通常实现为将 i 的值从内存读取到寄存器中;增加寄存器;将值写入内存:



    r1 ← xglobal
    r1 ← r1 + 1
    xglobal ← r1
  

So the value of xglobal is incremented by one.

如果您有两个并行线程,那么它们可能会破坏性地交错



    initial xglobal = 99

    r1 ← xglobal     
    compare r1 100 
                    r2 ← xglobal          
                    compare r2 100 
    r1 ← r1 + 1  == 100 
                    r2 ← r2 + 1 == 100
    xglobal ← r1    == 100
                    xglobal ← r2     == 100
    r1 ← xglobal     
    compare r1 100 
    stop
                    r2 ← xglobal     
                    compare r1 100 
                    stop
    final xglobal = 100
  

So the value of xglobal is incremented by one, despite both threads incrementing it.

(我忽略了缓存的影响,这意味着线程 2 中变量的读取可以表现得好像它发生在线程 1 中的写入之前,即使线程 1 的写入发生在挂钟时间读入之前。在 pthread 中获取和释放互斥体会导致内存屏障,迫使所有读取行为都发生在获取或释放之后,而写入行为就像发生在获取或释放之前。)

(上面相当于for ( int r; ( r = x ) < 100; x = r + 1 )而不是for (; x < 100; x = x + 1 )这可能会对 x 进行额外的读取,因此还有另一个线程可能干扰的点)

类似地,一个线程的增量可能会破坏另一个线程的增量,从而允许线程以 i



    initial xglobal = 98
                    r2 ← xglobal          
    r1 ← xglobal     
    compare r1 100 
    r1 ← r1 + 1  == 99
    xglobal ← r1     
    r1 ← xglobal     
    compare r1 100 
    r1 ← r1 + 1  == 100
    xglobal ← r1     
    r1 ← xglobal     
    compare r1 100 
    stop
                    compare r2 100 
                    r2 ← r2 + 1 == 99
                    xglobal ← r2      
                    ...
    final xglobal = 99
  

因此,左线程的第二个增量被第一个增量覆盖,并且它将以全局可见值 x

您可能知道所有这些,并且可能希望使用一种机制来防范它。

我说“可能”是因为你的要求不清楚 - 当 x 达到 100 时上面的线程终止;要求没说,那里也没说。

So, since no thread will terminate without writing xglobal ← 100, the requirement may in fact be met without any locking, but x may be incremented n*100 times rather than 100 times. ( if the limit was larger than a byte then the writing of x might be non-atomic on some platforms, which could result in an infinite loop if bytes from different threads are mixed together, but for a limit of 100 that won't happen )

One technique is to use a mutex http://opengroup.org/onlinepubs/007908775/xsh/pthread_mutex_init.html which blocks other threads from running when one thread holds the lock on the mutex. If the lock is acquired before xglobal is read, and not released until xglobal is written, then the reads and writes of the thread cannot interlace.



    initial xglobal = 98

    lock (mutex) 
    mutex locked 
                    lock(mutex) 
                    blocked 
    r1 ← xglobal     
    compare r1 100 
    r1 ← r1 + 1  == 99
    xglobal ← r1     

    release ( mutex )
                    mutex locked

                    r2 ← xglobal          
                    compare r2 100 
                    r2 ← r2 + 1 == 100
                    xglobal ← r2      

                    release ( mutex )

    lock (mutex) 
    mutex locked 
    r1 ← xglobal     
    compare r1 100 
    release ( mutex )
    stop
                    ...
    final xglobal = 100
  

在 pthreads 之外,您可能想要使用平台的比较和交换操作(__sync_val_compare_and_swap in gcc http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Atomic-Builtins.html#Atomic-Builtins),它采用一个地址、一个旧值和一个新值,并且如果该地址处的内存等于旧值,则自动将其设置为新值。这使您可以将逻辑编写为:

for ( int v = 0; v < 100; ) {
    int x_prev = __sync_val_compare_and_swap ( &x, v, v + 1 );

    // if the CAS succeeds, the value of x has been set to is x_prev + 1
    // otherwise, try again from current last value
    if ( x_prev == v ) 
        v = x_prev + 1;
    else
        v = x_prev;
}

So if



    initial xglobal = 98
    initial v1  = 0
    initial v2  = 0

    cmp v1  100
    x_prev1 ← CASV ( xglobal, v1, v1 + 1 ) = 98 ( set fails with x == 98 )

                    cmp v2  100
                    x_prev2 ← CASV ( xglobal, v1, v1 + 1 ) = 98 ( set fails with x == 98 )

    v1 ← x_prev1 = 98 // x_prev != v
                    v2 ← x_prev2 = 98
                    cmp v2  100
                    x_prev2 ← CASV ( xglobal, v1, v1 + 1 ) = 98 ( set succeeds with x == 99 )

                    v2 ← x_prev2 + 1 = 99 // as x_prev == v

    cmp v1  100
    x_prev1 ← CASV ( xglobal, v1, v1 + 1 ) = 99 ( set fails with x == 99 )
    v1 ← x_prev1 = 99 // as x_prev != v

    cmp v1  100
    x_prev1 ← CASV ( xglobal, v1, v1 + 1 ) = 99 ( set succeeds with x == 100)
    v1 ← x_prev1 + 1 = 100 // as x_prev == v

                    cmp v2  100
                    x_prev2 ← CASV ( xglobal, v1, v1 + 1 ) = 100 ( set fails with x == 100 )

                    v2 ← x_prev2  = 100 // as x_prev != v
    cmp v1  100
                    cmp v2  100
    stop
                    stop
  

On each loop, xglobal will atomically be set to the value of r1 + 1 if and only if its previous value was r1; if not, r1 will be set to the value of xglobal tested during the CASV operation. This reduces the amount of time locks are held on most implementations ( though it still requires locking the memory bus for the duration of the CAS operation, only those operations will be serialised. As performing the CAS is expensive on multi-cores, it probably won't be much better for such a simple case as this. )

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

C 线程编程 - 增加共享变量 的相关文章

  • 赋值运算符和复制构造函数有什么区别?

    我不明白C 中赋值构造函数和复制构造函数之间的区别 是这样的 class A public A cout lt lt A A lt lt endl The copy constructor A a b The assignment cons
  • 添加对共享类的多个 WCF 服务的服务引用

    我正在尝试将我的 WCF Web 服务拆分为几个服务 而不是一个巨大的服务 但是 Visual Studio Silverlight 客户端 复制了两个服务共享的公共类 这是一个简单的例子来说明我的问题 在此示例中 有两个服务 两者都返回类
  • 如何进行带有偏差的浮点舍入(始终向上或向下舍入)?

    我想以偏置舍入浮动 要么总是向下 要么总是向上 代码中有一个特定的点 我需要这个 程序的其余部分应该像往常一样四舍五入到最接近的值 例如 我想四舍五入到最接近的 1 10 倍数 最接近 7 10 的浮点数约为 0 69999998807 但
  • 获取两个字符串之间的公共部分c# [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我需要的是获取两个单词之间的共同部分并获取差异 例子 场景1 word1 感言 word2 Test 将返回 公共部分Test 不同之
  • 当我单击 C# 中的“取消”按钮时重定向到新页面(Web 部分)

    Cancel button tc new TableCell btnCancel new Button btnCancel Text Cancel btnCancel Click new EventHandler btnCanel Clic
  • 捕获 foreach 条件中抛出的异常

    我有一个foreach在 foreach 本身的条件下循环期间中断的循环 有没有办法try catch抛出异常然后继续循环的项 这将运行几次 直到异常发生然后结束 try foreach b in bees exception is in
  • Linux TUN/TAP:无法从 TAP 设备读回数据

    问题是关于如何正确配置想要使用 Tun Tap 模块的 Linux 主机 My Goal 利用现有的路由软件 以下为APP1和APP2 但拦截并修改其发送和接收的所有消息 由Mediator完成 我的场景 Ubuntu 10 04 Mach
  • Guid 应包含 32 位数字和 4 个破折号

    我有一个包含 createuserwizard 控件的网站 创建帐户后 验证电子邮件及其验证 URL 将发送到用户的电子邮件地址 但是 当我进行测试运行时 单击电子邮件中的 URL 时 会出现以下错误 Guid should contain
  • 为什么 Web Worker 性能在 30 秒后急剧下降?

    我正在尝试提高在网络工作人员中执行时脚本的性能 它旨在解析浏览器中的大型文本文件而不会崩溃 一切都运行得很好 但我注意到使用网络工作者时大文件的性能存在严重差异 于是我做了一个简单的实验 我在同一输入上运行脚本两次 第一次运行在页面的主线程
  • 获取从属性构造函数内部应用到哪个属性的成员?

    我有一个自定义属性 在自定义属性的构造函数内 我想将属性的属性值设置为属性所应用到的属性的类型 是否有某种方式可以访问该属性所应用到的成员从我的属性类内部 可以从 NET 4 5 using CallerMemberName Somethi
  • VS30063:您无权访问 https://dev.azure.com

    我正在尝试在 asp net core 2 1 mvc 应用程序中使用以下代码连接 Azure DevOps Uri orgUrl new Uri https dev azure com xxxxx String personalAcces
  • 在 C# 中将位从 ulong 复制到 long

    所以看来 NET 性能计数器类型 http msdn microsoft com en us library system diagnostics performancecounter aspx有一个恼人的问题 它暴露了long对于计数器
  • 为什么 std::allocator 在 C++17 中丢失成员类型/函数?

    一边看着std 分配器 http en cppreference com w cpp memory allocator 我看到成员 value type pointer const pointer reference const refer
  • Xamarin Android:获取内存中的所有进程

    有没有办法读取所有进程 而不仅仅是正在运行的进程 如果我对 Android 的理解正确的话 一次只有一个进程在运行 其他所有进程都被冻结 后台进程被忽略 您可以使用以下代码片段获取当前正在运行的所有 Android 应用程序进程 Activ
  • 范围和临时初始化列表

    我试图将我认为是纯右值的内容传递到范围适配器闭包对象中 除非我将名称绑定到初始值设定项列表并使其成为左值 否则它不会编译 这里发生了什么 include
  • 两组点之间的最佳匹配

    I ve got two lists of points let s call them L1 P1 x1 y1 Pn xn yn and L2 P 1 x 1 y 1 P n x n y n 我的任务是找到它们点之间的最佳匹配 以最小化它
  • 在 C 中复制两个相邻字节的最快方法是什么?

    好吧 让我们从最明显的解决方案开始 memcpy Ptr const char a b 2 调用库函数的开销相当大 编译器有时不会优化它 我不会依赖编译器优化 但即使 GCC 很聪明 如果我将程序移植到带有垃圾编译器的更奇特的平台上 我也不
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 过度使用委托对性能来说是一个坏主意吗? [复制]

    这个问题在这里已经有答案了 考虑以下代码 if IsDebuggingEnabled instance Log GetDetailedDebugInfo GetDetailedDebugInfo 可能是一个昂贵的方法 因此我们只想在调试模式
  • WPF/数据集:如何通过 XAML 将相关表中的数据绑定到数据网格列中?

    我正在使用 WPF DataSet 连接到 SQL Server Express XAML 和 C Visual Studio 2013 Express 我从名为 BankNoteBook 的现有 SQL Server Express 数据

随机推荐

  • 如何从 Angular2 和 ng-bootstrap 组件中的 NgbTabSet 访问“select”方法?

    使用 Angular 2 3 1 和 ng bootstrap 1 0 0 alpha 18 我正在尝试以编程方式根据组件中的 ID 而不是模板内的 ID 选择选项卡 目标是从 url 中提取参数并使用它来选择 ngOnInit 中的选项卡
  • 在 Javascript 中从本地数据保存文件

    场景如下 用户来到我的网站并打开一个带有一些 JavaScript 功能的网页 用户通过javascript编辑数据 用户单击保存按钮来保存数据 事情是 他们似乎不需要下载这些数据 因为它已经在本地计算机上的 JavaScript 中 是否
  • 用于检测 .NET CF 3.5 并安装它的 Windows Mobile Cab 设置

    我使用 NET CF 3 5 等目标框架和 professional 6 SDK 开发了 windows mobile 6 professional 应用程序 还创建了其 SmartDeviceCab 文件 当我将其安装在没有 CF 3 5
  • 如何控制.NET SoapFormatter中的命名空间?

    我正在编写一些需要向后兼容使用 SOAP 序列化某些对象的现有远程处理代码的代码 我的困难是我必须将一些对象移动到新程序集 因此远程处理被破坏 例如 我使用 NET SoapFormatter 序列化一个对象 如下所示 Person p n
  • vim 正则表达式用于替换引号内的空格

    我有以下格式的文本 ERR OUT OF MEM ERR OUT OF MEM ERR SOMETHING BAD ERR SOMETHING BAD 我想用下划线替换文本中引号内的所有空格 ERR OUT OF MEM ERR OUT O
  • MVVM 最佳实践:视图模型之间的通信

    我的简化程序结构如下所示 public class Manager public Item MyItem get set public void Recalculate public class Item public string Som
  • 每对观测值的马氏距离

    我正在尝试计算数据集的每个观测值之间的马哈拉诺比斯距离dat 其中每行是一个观察值 每列是一个变量 该距离定义为 我写了一个函数来做到这一点 但我觉得它很慢 在 R 中是否有更好的方法来计算它 生成一些数据来测试该功能 generateDa
  • 这个正则表达式不应该发生灾难性的回溯

    有人可以解释为什么 Java 的正则表达式引擎会在此正则表达式上进入灾难性的回溯模式吗 据我所知 每个交替都与其他每个交替相互排斥 s s Text p o de a car itaucard mastercard platinum SUS
  • 如何在Python 3.6中执行2个协程

    我无法让两个协程在我的 Python 3 6 程序中并行执行 这是一个例子 import asyncio time def main loop asyncio get event loop loop run until complete s
  • 查找C++静态初始化顺序问题

    我们遇到了一些问题静态初始化顺序惨败 http www parashift com c faq lite static init order html 并且我正在寻找方法来梳理大量代码以查找可能发生的情况 关于如何有效地做到这一点有什么建议
  • Cypher 查询 JSON 格式的结果

    在演员 电影演示图上 cypher 在单独的数组中返回列名称 MATCH n Person RETURN n name as Name n born as Born ORDER BY n born LIMIT 5 results colum
  • Mysql查询查找具有相同列值的字段之和

    我有一张这样的桌子 id invent id order 1 95948214 70 2 46018572 30 3 46018572 20 4 46018572 50 5 36025764 60 6 36025764 70 7 95948
  • Java音乐播放器:歌曲信息和播放[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 在Android中 我们可以使用媒体播放器在设备上播放歌曲 并使用光标来获取曲目信息 操作系统跟踪的信
  • 简单登录返回空白页

    我正在学习 PHP 并且制作了一个简单的登录脚本 但问题是它仅将我重定向到空白页面 如果用户凭据正确 它的意思是重定向到index php 但情况显然并非如此 还有验证 如果用户输入空白 则会返回错误 这似乎没有被执行 登录 php
  • 使用 rdmsr/rdpmc 提高分支预测精度

    我试图了解分支预测单元在 CPU 中如何工作 我用过papi还有linux的perf events但他们都没有给出准确的结果 对于我的情况 这是我的代码 void func int arr int sequence len for int
  • 在c中获取浮点数的指数

    抱歉 如果已经有人问过这个问题 并且我已经看到了提取浮点数指数的其他方法 但这就是给我的 unsigned f2i float f union unsigned i float f x x i 0 x f f return x i 我无法理
  • css4 中可以使用父选择器吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 Here is fiddle http jsfiddle net uday redI V9F7d 7 我有 2 个 div 一个是外盒 另一个是
  • Matplotlib 在 vi​​rtualenv 中不显示图形

    我已经在我的 virtualenv 中安装了 pip matplotlib 并且正在尝试绘制一个简单的图表 我使用 Eclipse 和 PyDev 当我从 Eclipse 运行脚本时 它根本不显示任何图形 我已经尝试过其他问题中提出的建议
  • 是否可以在 Cloudformation 中更新 Elastic Beanstalk 环境而不影响部署到其中的版本?

    我正在使用 Cloudformation 创建 Elastic Beanstalk 环境 我必须创建一个 ApplicationVersion 只是为了启动它并将其输入到环境的定义中 我创建其他应用程序版本并以其他方式将它们部署到集群 Co
  • C 线程编程 - 增加共享变量

    嘿伙计们 所以我正在尝试温习我的 C 线程 我发现的一个问题是 给定一个全局变量 int x 0 实现函数 void 无用 int n 它创建 n 个线程 在循环中将 x 加 1 每个线程在 x 达到 100 时终止 我只是没有掌握线程 需