65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (31投票s)

2012年5月22日

CPOL

4分钟阅读

viewsIcon

110775

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

我对 C# 线程的疯狂而盲目的爱

当我第一次看到 C# 线程代码时,我立刻爱上了它。随着时间的推移,“System.Threading”命名空间的强大和简洁将我的爱变成了盲目的爱。

Thread t1 = new Thread(SomeThread);
t1.Start(o1);

如果您还没有爱上它,并且是 C# 线程的新手,您可以看看我的 C# 线程培训视频。

盲目的爱是显而易见的,因为在当今现代世界,并行任务执行是一个必不可少的组成部分。我的爱帮助我用令人惊叹的 UI 界面和多线程批处理程序来取悦用户,直到有一天……

与 C# 线程的盲目之爱的终结

致谢:http://openclipart.org/

但有一天,我对 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 以查看预期的行为。

亲身体验并看看

如果您想亲眼看看我在这篇博客中说过的内容,请观看下面的视频

您也可以通过下载代码来亲自查看。

进一步阅读,请观看下面的面试准备视频和分步视频系列。

© . All rights reserved.