使用任务时必须使用锁吗?

时间:2019-04-11 06:30:01

标签: c# locking

我目前正在WinForm C#中开发一个应用程序以显示设备中的值,但是我需要找到正确的方法。

我在Mainform构造函数中启动了一个无限循环的任务。此任务是从设备读取数据并将其写入共享变量。

Mainform正在读取这些共享变量以显示数据。

由于任务仅在写入而Mainform仅在读取共享变量,我需要对共享变量使用锁定指令吗? 如果不使用Lock,会有什么风险?

下面是Mainform构造函数中任务的开始。

谢谢!

Task.Run(() =>
{
    while (true)
    {
        try
        {
            ReadDeviceData();
        }
        catch (Exception e)
        {
            Dispatcher.CurrentDispatcher.Invoke(() => 
                MessageBox.Show(e.Message +"\n\n"+ e.StackTrace, "task exception\n"));
        }                        

        Thread.Sleep(200);
    }
});

4 个答案:

答案 0 :(得分:0)

如果只有一个线程写入变量,那么您不必锁定。同时尝试写入一个变量时,建议使用锁。

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement

答案 1 :(得分:0)

这取决于ReadDeviceData

在C#的内存模型中,读取intboolobject('s reference)是安全的。

在其他情况下,您可以考虑使用ReaderWriterLockReaderWriterLock,这更适合您的情况。

答案 2 :(得分:0)

lock关键字不会锁定变量。它定义了一部分代码,一次只能在一个线程上执行。例如,在这两个代码块中,任何时刻都只能执行一个,并且只能在一个线程上执行:

lock (lockObject)
{
    this.A = random.Next();
    this.B = random.Next();
    this.C = this.A + this.B;
}

lock (lockObject)
{
    Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
}

因此,在任何给定时刻,只有一个线程正在读取或写入成员变量A,B和C。

如果我们卸下锁,会发生什么?操作系统使用抢占式多线程,因此您不能确保在任何两行代码之间(有时甚至在执行过程中)任何时候都不会中断您的代码。因此,例如,第二个代码块可能出现在第一个代码块中的任何两行之间。让我们看看...

this.A = random.Next();
this.B = random.Next();
Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
this.C = this.A + this.B;

您可以清楚地看到,该程序在数学上会很糟糕。

话虽如此,我们通过巧妙地分配事物,可以毫无问题地缓解问题。

var results = new SomeClass
{
    A = random.Next(),
    B = random.Next()
};
results.C = results.A + results.B;
this.LatestResults = results;

现在,唯一共享的变量(因为每个线程都有自己的局部变量)是LatestResults。可以通过单个原子操作进行更新。在这种情况下,不需要锁定。

答案 3 :(得分:0)

除非您是无锁多线程编程的专家,否则在阅读时应始终锁定,除非您不问这个问题。不带锁的线程有很多警告,所以最好不要后悔。

请记住,毫无争议的锁很便宜。您可以在一秒钟内锁定一百万次,而您的CPU甚至不会注意到。争执时锁成为瓶颈。如果您的UI偶尔访问共享数据,而不是处于紧密循环中,则使用锁应该没有问题。在这种情况下,没有理由寻找无锁解决方案。