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

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

我有两个线程,并且这些线程之间共享了一个

float
类型的值。浮点型值写入
Thread1
并通过
Thread2.
读取
Thread1
Thread2
同时开始。

 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
进入
for
中的
Thread2
循环时,已被锁定并且
Thread1
无法在其中写入新值。

我正在尝试找出解决方案。我正在尝试

lock
Monitor
ConcurrentQueue
,但每次我都得到相同的结果。
BufferData
仅由一个数字填充。

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

enter image description here

c# multithreading object share lock-free
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(boundedCapacity:100);
private void Thread1()
{
     Random ran = new Random();
     while (!_stop)
     {
         queue.Add( 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.