我有一个具有用户界面的程序。
当用户按下按钮时,程序将加载一些数据,这将需要一分钟左右,因此接口自然无法更新。
所以,我正在做以下事情 - 我有一个名为" hasLoaded"的布尔变量。加载代码(处理它自己的线程)将在完成时设置为true。界面将经常检查并停止显示“正在加载”。屏幕。
问题是 - 因为这是一个共享变量,你可能想要锁定它(或使用读者/作者瘦锁或其他东西)。锁显然只是共享变量(" hasLoaded")。但是从那以后:
我还需要锁吗?澄清 - 是否有任何数据损坏'如果两个线程同时尝试读取它,或者是“最差”的问题,可能会发生警告或其他问题。可能发生的事情是它读取错误的值一次并在下一次正确地选择它?
答案 0 :(得分:1)
由于数据类型小于或等于CPU架构的字大小,因此无需担心是否存在数据损坏问题。写入和读取将是原子的。
但这不是问题。问题在于,在某些情况下,该标志的读者可能永远不会将其更改为true
值。使用以下代码实际上很容易演示。您需要使用Release配置对其进行编译,并在不连接调试器的情况下运行它。您可能会发现该程序永远无法有效地证明该错误。
// * Must be compiled as RELEASE and ran outside of a debugger.
class Program
{
// Decorate with volatile to change the behavior.
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(() =>
{
Console.WriteLine("thread begin");
bool toggle = false;
while (!stop)
{
toggle = !toggle;
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
// The Join call should return almost immediately.
// With volatile it DOES.
// Without volatile it does NOT.
t.Join();
}
}
在您的具体情况下,这可能实际上没有实际意义。原因是作者是后台线程而读者是UI线程。这在这里至关重要,因为内存屏障将保证在后台线程结束时提交写入,这可能是在hasLoad
设置为true
之后。在UI线程方面,消息泵本身可能会注入一个你不知道的内存障碍。因此,每次检查hasLoaded
的值(可能是某种计时器)时,您可能会获得最新值。
无论如何,如果它没有锁定或使用volatile
,它只是偶然地这样做。帮自己一个忙,采取适当的保护措施,然后锁定。
更好的是,将Task
与新的async
和await
关键字结合使用。如果操作正确,即使不需要hasLoaded
标志也可以完成,并且它看起来也会更优雅。
答案 1 :(得分:0)
您需要将布尔值声明为volatile。
如MSDN所述:
volatile关键字表示字段可能被修改 多个线程同时执行。是的领域 声明的volatile不受编译器优化的限制 假设由单个线程访问。这确保了最多 字段中始终存在最新值。
如果你没有将它声明为volatile,编译器可能会优化你的代码,使得另一个线程永远不会看到标志的更新。
答案 2 :(得分:-1)
您不需要锁定示例的布尔值,但是如果您可能想要进行代码注释以指示您选择不锁定的原因,那么您可能会重新访问此代码并对'提出不同的期望。脏'读取共享布尔值。