被遗忘的英雄,volatile 关键字: - C# 线程






4.69/5 (31投票s)
被遗忘的英雄,Volatile 关键字: - C# 线程
我对 C# 线程的疯狂而盲目的爱
当我第一次看到 C# 线程代码时,我立刻爱上了它。随着时间的推移,“System.Threading
”命名空间的强大和简洁将我的爱变成了盲目的爱。
Thread t1 = new Thread(SomeThread);
t1.Start(o1);
如果您还没有爱上它,并且是 C# 线程的新手,您可以看看我的 C# 线程培训视频。
盲目的爱是显而易见的,因为在当今现代世界,并行任务执行是一个必不可少的组成部分。我的爱帮助我用令人惊叹的 UI 界面和多线程批处理程序来取悦用户,直到有一天……
与 C# 线程的盲目之爱的终结
但有一天,我对 C# 线程的爱遇到了阻碍。我有两个线程在运行,并且都在更新同一个局部变量。一个线程的更新对另一个线程不可见。
让我详细说明一下。我创建了一个名为“_loop
”的局部变量,如下面的抽象代码片段所示。默认情况下,此变量设置为“true
”。
class Program
{
bool _loop = true;
…
…
}
在同一个应用程序中,我创建了一个名为“SomeThread
”的函数,该函数将一直运行,直到“_loop
”为 true
。
private static void SomeThread(object o1)
{
….
….
while (o._loop)
{}
}
从主线程中,我正在派生一些线程,这些线程一直在运行循环。在这个主线程中,我将“_loop
”设置为 false
。换句话说,“SomeThread
”的无限循环应该停止并退出……哇,它没有。
static void Main(string[] args)
{
Thread t1 = new Thread(SomeThread);
t1.Start(o1);
o1._loop = false;
….
….
}
以下是完整的源代码,您可以下载并亲自查看。
bool _loop = true;
static void Main(string[] args)
{
Program o1 = new Program();
Thread t1 = new Thread(SomeThread);
t1.Start(o1);
Thread.Sleep(2000);
o1._loop = false;
Console.WriteLine("Value Set to false");
}
private static void SomeThread(object o1)
{
Program o = (Program)o1;
Console.WriteLine("Loop Starting...");
while (o._loop)
{}
Console.WriteLine("Loop Stopping...");
}
如果您运行上面的代码,您可以看到无限循环开始,但即使该值设置为“false
”,它也不会结束。
您还可以通过单击 C# 线程培训来查看该代码的实际视频演示。
注意:运行代码时,请确保已选择 release 并按 CNTRL + F5。
为什么我的爱抛弃了我:本地线程内存存储和同步
经过多年的成功关系,很难相信这种意外的行为。所以我开始在 Google 上搜索原因。
经过长时间的阅读,我震惊地发现,在线程处理方面,我们需要处理两种内存。
一个是我们的变量被分配到的主内存,线程也有自己的本地内存……惊讶吗?我也是。
换句话说,“_loop
”存在于三个地方,一个在主内存中,第二个在“mainthread
”和“somethread
”函数的本地内存中。
当程序启动时,“_loop
”设置为 true
。因此,所有内存存储都将“_loop
”设置为 true
。请参见下面的可视化表示。
当程序运行时,主线程将值设置为 false。此更新是在“mainthread”的本地内存和主内存中完成的,但它不会更新“SomeThread”的本地内存。因此,“SomeThread”函数仍然看到过时的值并继续循环运行。
以下是内部在各个点的外观的可视化表示。
被遗忘的英雄:Volatile 关键字
所有关系都有问题,我们只需要调整,找到解决方案并继续生活。在经历了如此美好的关系之后,我绝对不想离开 C# 线程。
解决这种多重内存存储的方案是“VOLATILE
”关键字。
如果您将变量声明为 volatile
,则线程会尝试从主内存访问值,而不是从数据过时的本地内存访问值。它确实会慢一点,但它解决了这个大问题。
亲自看看,采用相同的源代码并将“_loop
”变量更改为 volatile
,如下面的代码片段所示并运行它。
class Program
{
volatile bool _loop = true;
确保选择“release
”模式并按 control + F5 以查看预期的行为。
亲身体验并看看
如果您想亲眼看看我在这篇博客中说过的内容,请观看下面的视频
您也可以通过下载代码来亲自查看。
进一步阅读,请观看下面的面试准备视频和分步视频系列。