C#使用类作为存储空间并从不同的线程访问它是否安全?

时间:2016-05-23 12:48:31

标签: c# multithreading class

说,我有像这样的静态

static class PCstatus
    {
        public static class Cpu
        {
            //CPU loads
            public static int lt;
            public static int l1;
            public static int l2;
            public static int l3;
            public static int l4;
            //CPU Temp
            public static double t0;
            //Frequency
        }}

我将其用作存储空间(我应该这样做吗?)

我有5-6个线程定期更改此类中的不同变量(注意:没有两个线程更改相同的值),即:

第一个帖子:

PCstatus.lt = 0;,
thread.sleep(1000);

第二

PCstatus.l1 = 0;,
thread.sleep(1000);

然后我有另一个线程定期读取类中的所有值,解析它们并通过串行发送它们。

这是一种理智的方式吗?类中没有锁定机制,因此理论上,其中一个线程可以尝试在最终线程读取时更改var。

我不确定是否会发生这样的事情,我已经运行了这个程序好几天了。到目前为止,还没有注意到任何奇怪的行为。

我可以为类实现一个锁定机制。 (bool _isBeingUsed)并让线程在执行任何操作之前检查该值,但我不确定是否有必要。

我知道从线程输出值的正确方法是使用委托,但如果它不是真的必要,我可以做到没有他们带来的额外复杂性。

3 个答案:

答案 0 :(得分:1)

对C#中int值的读写是原子的,因此您永远不必担心数据剪切。

但是,在类中写入多个值不是原子的,因此在您的示例中:

第一个帖子:

PCstatus.lt = 0;
thread.sleep(1000);

第二

PCstatus.l1 = 0;
thread.sleep(1000);

由于线程3认为lt为0,因此它也会看到l1为零,因此无法保证。您可能在此处遇到数据争用问题。

另外,仅仅因为一个线程写入一个变量,它并不意味着其他线程会立即看到它的值。指令的指令重新排序,指令的编译器重新排序和CPU缓存策略可能合谋阻止写入回到主存储器并进入另一个线程。

如果您只是要从一个线程更改单个值,那么请使用Interlocked类上的方法来确保您的更改在线程中可见。它们使用内存屏障来确保对变量的读/写传播跨线程。

如果您要在一次点击中写入多个值,或者如果您想在一次点击中读取多个值,那么您需要使用锁定。

答案 1 :(得分:0)

不需要锁定,但您应该声明这些字段volatile,以确保其他线程可以立即获取来自一个线程的更新。

请参阅:https://msdn.microsoft.com/en-us/library/x13ttww7.aspx

请注意,您无法将double声明为volatile。我认为对于您的应用程序,您可能只需使用float。否则,您可以使用包含不可变double值的类。

答案 2 :(得分:0)

  

我有5-6个线程,可以定期更改此类中的不同变量

您可以为每位员工提供事件,而不是为5-6名员工提供单一存储空间。然后,任何需要结果的人都可以订阅它并创建本地存储,这意味着不再有线程问题。

这样的东西
public static class CPUStats
{
    public static EventHandler<CPUEventArgs> Measured;

    public static CPUStats()
    {
        Task.Factory.StartNew(() =>
        {
            while(true)
            {
                ... // poll CPU data periodically
                Measured?.Invoke(null, new CPUEventArgs() { LT = lt, L1 = l1, ... });
            }
        }, TaskCreationOptions.LongRunning);
    }
}

public static class StatsWriter
{
    static int lt;
    static int l1;
    ...

    public static StatsWriter()
    {
        CPUStats.Measured += (s, e) =>
        {
           lt = e.LT;
           l1 = e.L1;
        }
    }

    public static void Save()
    {
        var text = $"{DateTime.Now} CPU[{lt},{l1}...]";
        ... // save text
    }
}