pthread 竞争条件,可疑行为

2023-12-24

我编写了以下代码来演示同一进程的两个线程之间的竞争条件。

`

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

int c = 0;
void *fnC()
{
    int i;
    for(i=0;i<10;i++)
    {   
        c++;
        printf(" %d", c); 
    }   
}


int main()
{
    int rt1, rt2;
    pthread_t t1, t2; 
    /* Create two threads */
    if( (rt1=pthread_create( &t1, NULL, &fnC, NULL)) )
        printf("Thread creation failed: %d\n", rt1);
    if( (rt2=pthread_create( &t2, NULL, &fnC, NULL)) )
        printf("Thread creation failed: %d\n", rt2);
    /* Wait for both threads to finish */
    pthread_join( t1, NULL);
    pthread_join( t2, NULL);
    printf ("\n");
    return 0;

}

`

我运行了这个程序,并预计两个线程之间会出现竞争条件(但是,我知道竞争条件的可能性非常小,因为线程主函数非常小)。 我运行了 50000 次。以下是输出,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - 49657 times (no race condition)
1 3 4 5 6 7 8 9 10 11 2 12 13 14 15 16 17 18 19 20 - 244 times (race condition occurs)
2 3 4 5 6 7 8 9 10 11 1 12 13 14 15 16 17 18 19 20 - 99 times (race condition occurs)

问题是, 当输出 2 中出现竞争条件时,线程 1 打印 1 并被换出处理器,线程 2 进入。它开始工作,在线程 2 打印 11 后,它被换出,线程 1 进入。它必须打印12,而是打印 2(实际上应该缺少 2)。我不知道怎么办。请帮助我理解这里发生了什么。


你正在以 C 思维方式思考,但如果你想考虑竞争条件,你必须在较低的层面上思考。

在调试器中,您通常在单行代码上设置断点,并且可以通过单步执行程序来观察每行代码的执行情况。但这不是机器的工作方式,机器可能会为每一行代码执行多条指令,并且线程可以在任何地方中断。

让我们看看这一行。

printf(" %d", c);

在机器代码中,它看起来像这样:

load pointer to " %d" string constant
load value of c global
# <- thread might get interrupted here
call printf

因此,这种行为并不意外。您必须加载的值c在你打电话之前printf,所以如果线程被中断的话always有机会c已经过时了printf做任何事。除非你做点什么来阻止它。

修复竞争条件:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int c = 0;
void *func(void *param)
{
    int i;
    for (i=0; i<10; i++) {
        pthread_mutex_lock(&mutex);
        c++;
        printf(" %d", c);
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

什么是volatile do?

问题中的代码可以转换为汇编代码,如下所示:

load the current value of c
add 1 to it
store it in c
call printf

不需要重新加载c在它递增之后,因为 C 编译器可以假设除了当前线程之外没有其他人(没有其他线程或设备)更改内存。

如果你使用volatile,编译器将严格保留每个加载和存储操作,并且程序集将如下所示:

load the current value of c
add 1 to it
store it in c
# compiler is not allowed to cache c
load the current value of c
call printf

这没有帮助。实际上,volatile几乎没有帮助。大多数C程序员不明白volatile,并且对于编写多线程代码几乎没有用处。对于编写信号处理程序、内存映射IO(设备驱动/嵌入式编程)很有用,对于正确使用setjmp/longjmp.

脚注:

编译器无法缓存以下值c打电话给printf,因为据编译器所知,printf可以换c (c毕竟是一个全局变量)。有一天,编译器可能会变得更加复杂,它可能知道printf没有改变c,因此程序可能会崩溃得更严重。

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

pthread 竞争条件,可疑行为 的相关文章

随机推荐

  • Laravel - 未定义的偏移量 0 - collection.php

    我正在编写一个网络应用程序 后端是一个基于 Laravel 4 的 RESTFul 应用程序 我在使用特定控制器时遇到问题 BedsController php class BedsController extends BaseContro
  • RPY2:导入程序因 .Renviron 失败

    这是使用时应该发生的事情rpy2 robjects packages importr对于基础 R 包 例如stats gt gt gt from rpy2 robjects packages import importr gt gt gt
  • 量角器 - 描述未定义

    我正在使用 Protractor js 进行测试 当我尝试使用运行测试用例时 量角器 e2e main test spec js 我的conf js An example configuration file exports config
  • DatePicker 不更新 Android 中的 Textview

    当选择某个日期的日期选择器时 我试图更新文本视图 但它的textview 没有更新 我正在使用两个日期选择器来更新两个不同的文本视图 这是我更新 TextView 的代码 public class SearchingActivity ext
  • wxPython CheckListBox 与 HTML

    我正在使用 wxPython 创建一个 GUI 应用程序 现在我正在使用 wx CheckListBox 来显示带有复选框的选项 但我希望使用 HTML 来格式化 CheckListBox 中的文本 解决这个问题的最佳方法是什么 Repla
  • 如何滚动重启一群杂种集群

    任何人都知道一种通过 Capistrano 以 滚动 方式重新启动杂种集群的好方法 例如一次一个杂种 如果每个人都有一点等待时间 让杂种也能加载 Rails 应用程序 那就太好了 我已经做了一些搜索 但没有找到太多 所以在我自己深入研究 m
  • 我可以使用 boost 库来执行跨平台应用程序吗?

    boost c 库中是否有任何 WinAPI WinExec 模拟 我需要从我的程序运行可执行文件 并将参数传递给它 我应该为此使用任何其他跨平台库 还是自己处理我的程序编译的操作系统 重要的 请参阅末尾的 POSIX 系统更新
  • 为什么我会收到有关此代码示例的警告?什么才是正确的呢?

    我正在学习一些 C 并且正在阅读scanf from 本教程 http www tutorialspoint com c standard library c function scanf htm其中包含以下代码块 include
  • WCF NetTcpBinding 缓冲与流性能问题

    我编写了一个 WCF 服务 该服务应该使用 Streamed TransferMode 转换任何大小的文件NetTcpBinding and System IO Stream object 在运行性能测试时 我发现了明显的性能问题 然后我决
  • 如何设置带空格的环境变量?

    我需要使用批处理文件设置环境变量的值 我为此编写了脚本 echo off set value Hello world setx M srijani srijani value 它给出了错误 ERROR Invalid syntax Defa
  • 重命名字典键

    有没有一种方法可以重命名字典键 而无需将其值重新分配给新名称并删除旧名称键 并且不迭代 dict 键 值 的情况下OrderedDict https docs python org 3 library collections html co
  • JPA更新多对多删除记录

    我在两个实体之间有 ManyToMany 关系 当我在拥有方执行更新时 JPA 似乎从我的数据库中删除了所有链接记录并重新插入它们 对我来说这是一个问题 因为我有一个在删除记录之前触发的 MySQL 触发器 关于如何解决这个问题有什么想法吗
  • .NET 进程间通信的最佳选择是什么? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我应该使用命名管道还是 NET Remoting 与计算机上正在运行的进程进行通信 WCF是最好的选择 它支持许多不同的传输机制 https l
  • 是否可以在 c/c++ 中的 double 类型(8 字节)变量中存储 8 个字符(每个字符 1 字节)?

    我正在将一些旧版 Fortran77 代码迁移到 C C 在Fortran77代码中 如果从文件中读入8个字符 则可以将它们存储在real 8类型的变量中 没有问题 是否可以在 C 或 C 中做类似的事情 如果是这样 我该怎么做 我无法在互
  • 标签指向 PHP 动态生成的图像?

    是否可以将图像重定向到动态生成的图像 使用 PHP 我有一个动态创建的图像 它有一个扩展名 PHP 显然 并且该服务器不在我的控制之下 所以我想将 somename jpg 在我的服务器上 重定向到 remoteserver dynamic
  • Rails 4 - 当前布局的名称?

    我找到了许多关于 Rails 3 的资源 但没有找到关于 Rails 4 的资源 为了保持干燥 我们有一个定义一些元标记的方法 我想将布局包含在title param app controllers application controll
  • 在 Google bigquery 中转换数据 - 提取文本、将其拆分为多列并旋转数据

    我在大查询中有一些博客数据 我需要对其进行转换以使其更易于使用和查询 数据如下 我想提取并转换数据在 Results 之后的大括号内 蓝色 数据的形式为 d PQ KL d 结果数组中可以有 1 20 多个条目 我只对前 16 个条目感兴趣
  • ClassNotFoundException:尝试从命名空间“FOS\UserBundle”加载类“FOSUserBundle”

    在 Windows 10 上使用 WampServer 我按照官方文档通过 Symfony 安装 Sonata User Bundle 我收到以下错误消息 1 1 ClassNotFoundException 尝试加载类 FOSUserBu
  • 有没有办法在 Scala 中为泛型类型变量设置默认值?

    我想为变量设置一个默认值 但我的 Scala 编译器说 Error 20 16 unbound placeholder parameter val p T 这是代码 object InverseFunctionsExample extend
  • pthread 竞争条件,可疑行为

    我编写了以下代码来演示同一进程的两个线程之间的竞争条件 include