C# 中两个线程之间读写 float 类型

问题描述 投票:0回答:1

我有两个线程,并且这些线程之间共享了一个 float-type 值。 在 Thread1 上写入浮点型值并由 Thread2 读取。 Thread1 和 Threda2 同时启动。

     private const int BUFFER_SIZE = 65536 * 8;
     private float _readWriteValue = 0;
     private bool _stop = false;
    
     private void Thread1()
     {
         Random ran = new Random();
         while (!_stop)
         {
             _readWriteValue = ran. Next(1, 100);
         }
     }
     private void Thread2()
     {
         while (!_stop)
         {
             float[] BufferData  = new float[BUFFER_SIZE];
             for (int i = 0; i < BUFFER_SIZE; i++)
             {
                 BufferData[i] = _readWriteValue;
             }
             ProcessMethod(BufferData);
         }
     }
    
     private void ProcessMethod(float[] data)
     {
         // Do Something
     }

所以,当我检查 BufferData 时,它只填充了一个数字。例如,BufferData 只填充了 22。看起来当 _readWriteValue 进入 Thread2 中的 for 循环时,已被锁定,Thread1 无法在其中写入新值。 我正在尝试找出解决方案。我尝试使用 lock、Monitor 和 ConcurrentQueue,但每次都得到相同的结果。 BufferData 仅由一个数字填充。

我对多线程的理解有误吗? 我该怎么办?

enter image description here

c# multithreading object share
1个回答
0
投票

该示例不是线程安全的。每当您读取和写入任何共享值时,您都需要某种形式的同步。编译器基本上可以将您的示例重写为:

     private void Thread2()
     {
         var value = _readWriteValue;
         while (!_stop)
         {
             float[] BufferData  = new float[BUFFER_SIZE];
             for (int i = 0; i < BUFFER_SIZE; i++)
             {
                 BufferData[i] = value ;
             }
             ProcessMethod(BufferData);
         }
     }

仅将

volatile
添加到
_readWriteValue
可能有助于线程安全方面,但是何时以及是否 volatile 合适存在争议。 Eric Lippert 在原子性、波动性和不变性是不同的,第三部分

中写道

坦率地说,我不鼓励你创建一个不稳定的领域

另一种选择是插入内存屏障,但除非你真的知道你在做什么,否则我建议使用锁。

还有一个重大问题,即循环运行所需的时间可能相差很大。如果您想生成一次读取的单个值,您应该使用队列进行通信。有很多方法可以做到这一点,但一个简单的方法是使用阻塞集合:

private BlockingCollection<float> queue = new BlockingCollection();
private void Thread1()
{
     Random ran = new Random();
     while (!_stop)
     {
         queue.Enqueue( ran. Next(1, 100))
     }
    queue.CompleteAdding();
}
private void Thread2()
{
     int i = 0;
     float[] BufferData  = new float[BUFFER_SIZE];
     foreach(var value in queue){
          i = i + 1 % 10;
          BufferData[i] = value;
          if(i == 0){
              ProcessMethod(BufferData);
          }
    }
}

您可以指定队列的最大缓冲区大小,这样如果没有更多空间,线程1将阻塞,如果没有可用值,线程2将阻塞。

© www.soinside.com 2019 - 2024. All rights reserved.