首页 / 知识
关于多线程:何时在C#中使用volatile关键字?
2023-04-15 22:18:00

When should the volatile keyword be used in C#?谁能对C#中的volatile关键字提供很好的解释? 它可以解决哪些问题,哪些不能解决? 在什么情况下可以节省锁定的使用时间? 我认为没有比埃里克·利珀特(Eric Lippert)更好的人了(原文强调):
有关更多阅读,请参阅:
如果您想稍微了解volatile关键字的功能,请考虑以下程序(我使用的是DevStudio 2005):
使用标准的优化(发布)编译器设置,编译器将创建以下汇编器(IA32):
查看输出,编译器已决定使用ecx寄存器存储j变量的值。对于非易失性循环(第一个),编译器已将i分配给eax寄存器。非常坦率的。但是,有几个有趣的位-lea ebx,[ebx]指令实际上是多字节nop指令,因此循环跳到16字节对齐的内存地址。另一个是使用edx来增加循环计数器,而不是使用inc eax指令。与inc reg指令相比,add reg,reg指令在几个IA32内核上具有较低的延迟,但从来没有更高的延迟。 现在使用易失性循环计数器进行循环。计数器存储在[esp],而volatile关键字告诉编译器应始终从存储器中读取/写入该值,并且永远不要将其分配给寄存器。甚至在更新计数器值时,编译器甚至不做为三个不同的步骤(加载eax,inc eax,保存eax)进行加载/增量/存储,而是直接在一条指令中修改内存(添加内存) ,reg)。代码的创建方式可确保在单个CPU内核的上下文中循环计数器的值始终是最新的。对数据的任何操作都不会导致损坏或数据丢失(因此,由于在inc期间可能会更改值,因此在存储上丢失,因此不使用load / inc / store)。由于仅在当前指令完成后才可以处理中断,因此即使内存未对齐,数据也永远不会损坏。 一旦在系统中引入了第二个CPU,volatile关键字将无法防止另一个CPU同时更新数据。在上面的示例中,您将需要对数据进行对齐以获取潜在的损坏。如果无法以原子方式处理数据,那么volatile关键字将不会防止潜在的损坏,例如,如果循环计数器的类型为long long(64位),那么它将需要两个32位操作来更新该值,位于可能发生中断并更改数据。 因此,volatile关键字仅适用于小于或等于本机寄存器大小的对齐数据,因此操作始终是原子的。 volatile关键字被认为可用于IO操作,在这些操作中IO会不断变化,但具有恒定的地址,例如内存映射的UART设备,并且编译器不应继续重复使用从该地址读取的第一个值。 如果要处理大数据或具有多个CPU,则需要更高级别(OS)的锁定系统来正确处理数据访问。 如果使用的是.NET 1.1,则在进行双重检查锁定时需要volatile关键字。为什么?因为在.NET 2.0之前,以下情况可能导致第二个线程访问非空但尚未完全构造的对象: //if(this.foo == null) //锁定(this.bar) //if(this.foo == null) //this.foo = new Foo(); 在.NET 2.0之前,可以在构造函数完成运行之前为this.foo分配新的Foo实例。在这种情况下,第二个线程可能会进入(在对Foo的构造函数的线程1调用期间),并且会遇到以下情况: //if(this.foo == null) //this.foo.MakeFoo() 在.NET 2.0之前,您可以将this.foo声明为volatile,以解决此问题。从.NET 2.0开始,您不再需要使用volatile关键字来完成双重检查锁定。
Wikipedia实际上有一篇关于Double Checked Locking的好文章,并简要介绍了该主题: 有时,编译器会优化字段并使用寄存器来存储它。如果线程1对字段进行写操作,而另一个线程访问该字段,则由于更新存储在寄存器(而不是内存)中,因此第二个线程将获得陈旧数据。 您可以认为volatile关键字是对编译器说的:"我希望您将此值存储在内存中"。这样可以保证第二个线程检索最新值。
从MSDN:
CLR喜欢优化指令,因此,当您访问代码中的字段时,它可能并不总是访问该字段的当前值(它可能来自堆栈等)。将字段标记为 您显然会失去一些优化,但这确实使代码更简单。
综上所述,对该问题的正确答案是: 编译器有时会更改代码中语句的顺序以对其进行优化。通常,在单线程环境中这不是问题,但在多线程环境中则可能是问题。请参见以下示例:
如果运行t1和t2,则不会输出任何结果或出现" Value:10"。可能是编译器在t1函数内部切换了行。如果然后执行t2,则可能是_flag的值为5,但_value的值为0。因此可能会破坏预期的逻辑。 要解决此问题,您可以使用可应用于字段的volatile关键字。该语句禁用编译器优化,因此您可以在代码中强制使用正确的顺序。
仅在真正需要时才应使用volatile,因为它会禁用某些编译器优化,这会损害性能。并非所有.NET语言都支持它(Visual Basic不支持它),因此它阻碍了语言的互操作性。
多个线程可以访问一个变量。 |
最新内容
相关内容
linux解释命令解释符?
linux解释命令解释符?,系统,数据,名称,基础,工作,工具,状态,命令,脚本,进程,Linux中的sh命令的详细解释sh是linux中运行shell的命令,是shell的linux多线程下载命令?
linux多线程下载命令?,软件,工具,平台,中心,系统,代理,网络,网站,手机,官方网站,从linux服务器中下载或上传文件wget是linux下一个从网络上自linuxls命令解释?
linuxls命令解释?,信息,系统,标准,命令,时间,名称,数据,文件,目录,观察,LS(LINUX中ls命令)详细资料大全ls命令是linux下最常用的命令之一,ls跟dlinux锁定帐户的命令?
linux锁定帐户的命令?,系统,密码,情况,管理,工作,用户,帐户,账户,命令,权限,Linux下如何禁止用户登录可以使用usermod命令来禁用用户账户。该linux锁定终端命令?
linux锁定终端命令?,工作,密码,地址,系统,信息,命令,图片,状态,目录,用户,linux基本命令1、linux系统常用操作命令如下:ls:全拼list,功能是列出目linux各种命令的解释?
linux各种命令的解释?,地址,工作,系统,信息,命令,目录,时间,管理,控制台,常用命令,linux的常用命令有哪些呢???希望带上解释date:打印或者设置linux查看多线程命令?
linux查看多线程命令?,系统,第一,线程,地址,数据,进程,命令,名称,软件,情况,如何查看linux服务器的cpu数量,内核数,和cpu线程数如何查看linuxlinux命令行锁定界面?
linux命令行锁定界面?,系统,管理,地址,密码,工作,一致,数字,首页,快捷键,界面,linux系统基本操作首先,您需要了解Linux系统的文件系统和目录结linux路径命令解释?
linux路径命令解释?,系统,信息,设备,数据,工具,命令,文件,标准,发行,时间,linux查看路径命令1、linux命令如果记不得,可以使用man命令来查看某linux锁定密码命令?
linux锁定密码命令?,密码,系统,管理,信息,策略,状态,名称,软件,位置,环境,如何在Linux上使用命令行管理密码1、Linux修改密码用 passwd 命令,用linux锁定用什么命令?
linux锁定用什么命令?,地址,系统,认证,图片,名字,信息,状态,时间,命令,用户,Linux实用命令有哪些?linux常用命令如下:查看内核版本:uname-a。控linux暂时锁定命令?
linux暂时锁定命令?,密码,标准,状态,代码,图片,命令,信息,单位,名字,管理,linux尝试登录失败后锁定用户账户的两种方法使用passwd命令重置用户