C# 如果我只有一个读写器,并且它';这不是时间限制吗?
我有一个有用户界面的程序 当用户按下按钮时,程序将加载一些数据,这将花费一分钟左右的时间,因此界面自然无法更新 因此,我将执行以下操作-我有一个名为“hasload”的布尔变量,加载代码(在它自己的线程上工作)在完成时将其设置为true。界面将不时检查并停止显示“加载”屏幕 问题是-由于这是一个共享变量,您可能希望锁定它(或者使用读写器slim锁或其他东西)。锁显然只围绕共享变量(“hasload”)。然而,由于:C# 如果我只有一个读写器,并且它';这不是时间限制吗?,c#,concurrency,locking,C#,Concurrency,Locking,我有一个有用户界面的程序 当用户按下按钮时,程序将加载一些数据,这将花费一分钟左右的时间,因此界面自然无法更新 因此,我将执行以下操作-我有一个名为“hasload”的布尔变量,加载代码(在它自己的线程上工作)在完成时将其设置为true。界面将不时检查并停止显示“加载”屏幕 问题是-由于这是一个共享变量,您可能希望锁定它(或者使用读写器slim锁或其他东西)。锁显然只围绕共享变量(“hasload”)。然而,由于: 只有一个读者 只有一个作家 如果接口读取的“错误”值是可以的,因为它将在几分之一
我还需要锁吗?澄清-如果两个线程同时尝试读取数据,是否存在任何“数据损坏”警告或其他问题,或者可能发生的“最坏”情况是,它读取了一次错误的值,并在下一次正确拾取了该值?您需要将布尔值声明为volatile 如MSDN所述: volatile关键字表示字段可能被修改 同时执行的多个线程。是的字段 声明的volatile不受编译器优化的影响 假设通过单个线程进行访问。这确保了 字段中始终存在最新值
如果不将其声明为volatile,编译器可能会优化代码,使标记的更新对于其他线程永远都不可见。您不需要为示例锁定布尔值,但如果您希望生成代码注释以指示您选择不锁定的原因,因为您可能会重新访问此代码,并且对共享布尔值的“脏”读取有不同的期望。因为它是一种小于或等于CPU体系结构字长的数据类型,所以您没有必要担心是否会出现数据损坏问题。写入和读取将是原子的 但这不是问题所在。问题在于,在某些情况下,该标志的读取器可能永远不会看到它更改为
true
值。这实际上很容易用下面的代码演示。您需要使用发行版配置编译它,并在没有附加调试器的情况下运行它。您可能会注意到,程序永远不会停止有效地演示错误
// * 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线程方面,消息泵本身可能会为您注入一个未知的内存屏障。因此,每次检查hasload
的值时(可能使用某种计时器),您可能会得到最新的值
不管怎样,如果它在没有锁或使用volatile
的情况下工作,那么它这样做只是偶然的。帮你自己一个忙,采取适当的保护措施,把锁拿走
更好的方法是,将
任务
与新的异步
和等待
关键字结合使用。如果操作正确,甚至无需使用hasload
标志即可完成此操作,而且看起来也会更优雅。锁将阻止对跨线程共享的资源执行并发操作。您认为可能需要锁定的跨线程共享的资源是什么?变量本身。对不起,我想我已经说清楚了。我会很快地回顾我的问题,只需使用a并等待它触发RunWorkerCompleted事件,它被称为“过时数据”,尽管这不太可能(取决于实际代码等),但您在形式上是正确的。这是错误的。这个操作实际上并不安全。这引起了一个有趣的阅读。这正是我想要的答案。非常感谢。