为什么在C#中,队列会扰乱其元素中的数据?
我对队列的功能完全感到困惑。我正在尝试(但未能)写作 一个小型多线程应用程序,用于在C#中收集和显示数据。为什么在C#中,队列会扰乱其元素中的数据?,c#,multithreading,queue,C#,Multithreading,Queue,我对队列的功能完全感到困惑。我正在尝试(但未能)写作 一个小型多线程应用程序,用于在C#中收集和显示数据。 在通读并使用消费者/生产者 他描述的模式是,除了我的数据在未来似乎被打乱之外,我的大部分数据都能正常工作 队列在排队之前,我的对象中的字段具有以下值 时间戳=6 数据[]={493698452412544861} 在退出队列后,数据看起来像 时间戳=6 数据[]={6479119464777265405} 我不明白为什么数据字段中的值在出列后被更改? 我很困惑,所以我想我会把它扔出去,看看
在通读并使用消费者/生产者 他描述的模式是,除了我的数据在未来似乎被打乱之外,我的大部分数据都能正常工作 队列在排队之前,我的对象中的字段具有以下值 时间戳=6
数据[]={493698452412544861} 在退出队列后,数据看起来像 时间戳=6
数据[]={6479119464777265405} 我不明白为什么数据字段中的值在出列后被更改? 我很困惑,所以我想我会把它扔出去,看看是否有人能给我指出正确的方向来解决这个问题,或者给我指出一个不同的方向来继续
相关代码
用于数据存储的自定义对象 相关对象和字段。sensorData类是一个单独的类,用于存储我的计算
public class sensorData
{
public const int bufSize = 4;
public UInt16[] data = new UInt16[4];
public double TimeStamp = 0;
public int timeIndex = 0;
}
以下字段用于设置队列以及入队列和出队列线程之间的信号
EventWaitHandle wh = new AutoResetEvent(false);
Queue<sensorData> dataQ = new Queue<sensorData>();
object locker = new object();
将数据排入队列并将其写入DequeueDataTest.txt
6479119464777265405
更新1: 当前代码中锁的位置。
我已经编辑了代码,以便在将数据写入文件时锁定。所以我有锁的代码块如下 在CalculateAndEnqueueData()方法中
lock (locker) dataQ.Enqueue(dat);
wh.Set
lock(locker)
{
sw2.Write(j.ToString() + ", ");
foreach (UInt16 dd in dat.intData)
{
sw2.Write(dd.ToString() + ", ");
}
sw2.WriteLine();
}
在dequeueDataMethod()中,我有两个带锁的区域,第一个在这里
lock(locker)
if (dataQ.Count > 0)
{
data = dataQ.Dequeue();
if (data == null)
{
sw.Close();
return;
}
}
我假设它为if块中的代码锁定锁。第二个是我在这里写入文件的地方
lock (locker)
{
sw.Write(data.timeIndex.ToString() + ", ");
foreach (UInt16 dd in data.intData)
sw.Write(dd.ToString() + ", ");
sw.WriteLine();
if (icnt > 10)
{
sw.Close();
return;
}
this.label1.Text = dataQ.Count.ToString();
}
就这些
问题是由于您正在写入的StreamWriter没有同步。顺序不是连续的。是不是因为您在一次又一次地向同一个数组写入
UInt16[]intDist
?您不应该为每个sensorData
对象使用单独的数组吗?(顺便说一句,在示例代码中是sensorData.intData
假设是sensorData.data
)
澄清:
在CalculateAndQueueData()
中只创建了一个intDist
数组,因此不同的sensorData
实例都共享同一个数组---如果在锁定步骤中发生添加+写入和删除+写入,则这是正常的;否则,某些数据点可能丢失/重复
建议:
直接填充sensorData
实例,而不使用intDist
数组,在calculateAndEnqueueData()中:
//创建新的sensorData实例
sensorData dat=新的sensorData();
dat.TimeStamp=时间;
dat.timeIndex=j;
//计算四条正弦曲线
对于(int i=0;i
当然,您可以让代码变得越来越简单,并且仍然可以演示您看到的问题。您正在锁定队列,但是StreamWriter上的同步在哪里???每个线程都写入一个单独的文件。因此,我认为我不需要对线程的这一部分进行同步。我错了吗?在dequeueDataMethod()中方法,我只看到一个文件。是的,dequeueDataMethod写入test.txt,CalculateAndQueueData写入test2.txt。是的,我需要在写入文件的代码周围放置一个锁。谢谢。这正是我所需要的!我以为你说每个线程都在写入一个单独的文件?我不认为StreamWriter的同步,这是的,每个线程都会写入不同的文件(test.txt和test2.txt)。我所知道的是,锁定代码块周围的锁似乎有帮助。我不知道为什么。这似乎只起作用,因为intDist是“作为块”写入和读取的…但是我认为这仍然是不正确的。要了解这一点,请在CalculateAndQueueData()中放置2秒延迟,在dequeueDataMethod()中放置4秒延迟然后看看会发生什么。@Zach Scrivena:好的,按照你的建议添加了延迟。代码似乎有效。我让它运行了大约1分钟,它的行为很好。两个文件都是相同的。是的,谢谢。我的代码被删减了。最初,sensorData有几个字段被使用,我在这个问题中删除了。不,我不这么认为,因为e生产者线程CalculateAndQueueData写入UInt16[]生成正确的数据。数据在消费者线程DequeueDataMethod中出列时被置乱,这是因为它们被“作为块”写入……出列时,相同的数组被修改(尽管对于不同的sensorData对象)通过计算DenQueueData,从而得出错误的结果。似乎您的代码是在锁定步骤中写入和读取sensorData对象——这就是目的吗?(检查队列长度是否超过一个……我怀疑没有)。是的,如果我减慢出队速度,队列长度将增加。因此队列长度可以增加到一个以上。
lock(locker)
if (dataQ.Count > 0)
{
data = dataQ.Dequeue();
if (data == null)
{
sw.Close();
return;
}
}
lock (locker)
{
sw.Write(data.timeIndex.ToString() + ", ");
foreach (UInt16 dd in data.intData)
sw.Write(dd.ToString() + ", ");
sw.WriteLine();
if (icnt > 10)
{
sw.Close();
return;
}
this.label1.Text = dataQ.Count.ToString();
}
// create new sensorData instance
sensorData dat = new sensorData();
dat.TimeStamp = time;
dat.timeIndex = j;
// Calculate four Sine curves
for (int i = 0; i < collectedData.numberOfChannels; i++)
{
dat.data[i] = (UInt16) Math.Abs(Math.Sin(2.0 * Math.PI * myFrequency[i] * time);
}
// enqueue
lock (locker) dataQ.Enqueue(dat);