C-克服原子操作的使用

如何解决C-克服原子操作的使用

我在想。如果我有一个要在所有线程中同步的int变量-我是否不能保留一位以了解该值是否正在更新?
为了避免写操作被分块执行,这意味着线程可能正在访问中间写入的值,这是不正确的,甚至更糟的是,将其覆盖,导致其完全错误,我希望首先通知线程该变量被写入。我可以简单地使用原子操作来写入新值,以免其他线程受到干扰,但是这种想法似乎并不愚蠢,我想首先使用基本工具。
如果我仅执行一个操作(该操作足够小以将其保留在一个块中),该操作就像更改单个位(仍将导致整个字节更改,但不是整个值更改一样),该怎么办? ,并让该位指示变量是否正在写入?甚至行得通,还是将整个int写入?
我的意思是,即使整个int发生了变化,这仍然有可能起作用-如果首先写出指示值是否正在变化的位。

对此有何想法?

编辑:我觉得我没有明确说明我实际上打算做什么,以及为什么我首先想到了这一点。
我正在尝试实现超时功能,类似于JavaScript中的setTimeout。超时非常简单,您永远都不想取消它-创建一个新线程,告诉它在给定的时间内休眠,然后为它提供一个执行函数,最终包含一些数据。小菜一碟。在大约半小时内完成了编写,而这对C来说是全新的。
当您要设置超时时可能会遇到困难,将来可能会取消该超时。因此,您所做的操作与超时完全相同而没有取消,但是在线程唤醒并且CPU的调度程序将其打开时,线程必须检查启动时给定的内存中的值是否不表示“您应该停止执行'。该值可能会被其他线程修改,但至少在最佳情况下只能执行一次。当试图同时修改多个线程的值时,我将担心不同的解决方案。现在的基本假设是只有主线程或其他线程之一才能修改该值,并且只会发生一次。只能通过设置其他变量来控制它发生一次,该变量可能会多次更改,但是始终更改为相同的值(即初始值为0,表示尚未取消,但是必须取消该变量) ,该值将更改为1,因此不必担心该值会被分成多个写操作,而在不同线程读取该值时,只需更新其中的一部分即可。
考虑到这一假设,我认为我最初在本文开头撰写的文字应该更加清晰。简而言之,不必担心该值会被任何线程多次写入,只能被一次写入,而且该值必须可以被任何其他线程读取,或者必须指出无法读取。 br /> 现在,正如我在思考的那样,由于值本身只会是0或1,所以知道何时取消它的技巧也应该起作用,不是吗?由于0或1将始终处于一个操作中,因此无需担心它会碎片化并读取不正确。如果我写错了,请纠正我。
另一方面,如果该值是从头开始而不是从头开始写的,那该怎么办?如果不可能,那么无需担心,该职位将得到解决,但是我想知道在这种特定情况下克服此类原子操作可能带来的每种危险。如果从头开始编写它,并且一个线程想要访问该变量以知道它是否应该继续执行,它将注意到它确实应该这样做,而预期的行为是停止执行。这应该有最小的可能性,但仍然是,这意味着危险,我希望它是100%可预测的。

另一个编辑说明了我想象程序要执行的步骤。
主线程产生一个新线程,也称为“可取消超时”。它传递一个函数以与数据,睡眠时间和内存地址一起执行,指向一个值。线程在给定时间后唤醒后,它必须检查该值以查看它是否应执行已赋予的功能。 0表示应继续,1表示应停止并退出。值(线程的“状态”,已取消还是未取消)可以由主线程或任何其他线程“超时”来操纵,其工作是取消第一个线程。
示例代码:

struct Timeout {
  void (*function)(void* data);
  void* data;
  int milliseconds;
  int** base;
  int cancelID;
};
DWORD WINAPI CTimeout(const struct Timeout* data) {
  Sleep(data->milliseconds);
  if(*(*(data->base) + sizeof(int) * data->cancelID) == 0) {
    data->function(data->data);
  }
  free(data);
  return 0;
}

其中CTimeout是提供给新产生的线程的函数。请注意,我已经在旅途中编写了其中一些代码,尚未对其进行测试。忽略任何潜在的错误。
Timeout.base是指向整数数组的指针,因为许多超时可以同时存在。 Timeout.cancelID是超时列表中当前线程的ID。如果将相同的ID视为基础数组中的索引,则它具有一个值。如果值为0,则线程应执行其功能,否则,请清理已提供的数据并正确返回。 base是指向指针的指针的原因是因为在任何时候都可以调整超时状态数组的大小。万一数组的位置发生变化,则无法传递其初始位置。可能会导致分段错误(如果不是,请纠正我),以访问不再属于我们的内存。
可以根据需要从主线程或其他线程访问Base,并且可以更改线程的状态以取消其执行。
如果有任何线程想要更​​改状态(该状态是我们一开始就生成的超时状态,并且想要取消),则应更改“基本”数组中的值。我认为到目前为止,这非常简单。
如果continuation和stopping的值大于1个字节,那将是一个巨大的问题。写入内存的操作实际上可能需要执行多项操作,因此,过早访问内存将导致发生意外的结果,这不是我喜欢的结果。但是,正如我前面提到的,如果该值很小(0或1)怎么办?在什么时候访问值根本不重要吗?我们只对1个字节,甚至2个或4个字节或整数感兴趣,即使8个字节在这种情况下也没有什么区别,对吗?最后,不必担心会收到无效的值,因为我们不在乎32位值,而只是在乎1位,无论我们要读取多少字节。
也许我的意思并不完全可以理解。写入/读取操作不包括读取单个位,而是读取字节。也就是说,如果我们的值不大于255或65535或4亿亿,无论我们正在写入/读取的字节数是多少,我们都不必担心在写入中间读取它。我们只关心正在写入的内容的一个块,即最后一个字节或第一个字节。其余的对我们完全没有用,因此无需担心在访问该值时所有这些都将被同步。真正的问题始于将值写入时,但写入的第一个字节在末尾,这对我们没有用。如果我们在那一刻读取该值,我们将收到不应该收到的信息-没有取消状态,而不是取消状态。如果首先读取的是低位字节序的第一个字节,那么即使在写入中间进行读取,我们也将收到有效值。
也许我正在搞弄和误解一切。我不是专业人士,你知道。也许我一直在阅读垃圾文章,无论如何。如果我在任何方面都做错了,请纠正我。

解决方法

除了某些具有专用硬件的专用嵌入式环境外,没有诸如“一项操作,该操作足够小以将其保留在一个块中,这种操作就像更改一位”之类的东西。您需要记住,您不想简单地用“ 1”(或“ 0”)覆盖特殊位。因为即使您可以执行此操作,它也可能与其他执行相同操作的线程重合。实际上,您需要做的是检查它是否已经为1,并且仅当您自己没有写1时,才知道您没有覆盖现有的1(或者写1失败,因为那里已经有1)。

这称为关键部分。而且这个问题只能由操作系统解决,它恰好知道或能够阻止其他并行线程。这是存在操作系统支持的同步方法的原因。

这没有简单的方法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-