为什么我的任务队列中的所有项目都被分配了相同的值
本文关键字:分配 项目 任务队列 我的 为什么 | 更新日期: 2023-09-27 18:17:56
在问这个问题之前,我一直在努力做尽职调查,但我似乎找不到我要找的东西。我认为我遇到了生产者-消费者的问题。我正在用c#编写一个winforms应用程序,它使用两个线程:一个用于UI,一个用于后台工作人员。项目通过提交按钮事件处理程序添加到任务队列。每当用户点击"提交"时)。如果队列中已经没有任何内容,则调用后台worker并开始处理队列。如果后台工作线程很忙,那么任务就会被添加到队列中。从理论上讲,当后台工作线程完成当前的工作后,它将转到队列中的下一个项目。
在我的UI线程中,我有以下代码实例化一个DiscQueue对象,然后将项目添加到它的队列:
private DiscQueue discQueue = new DiscQueue();
this.discQueue.AddToQueue(currentCD);
下面是我的DiscQueue类。我的AddToQueue函数将磁盘添加到队列中,然后在bw不忙时调用RunWorkerAsync()。然后,在bw_DoWork中,我从队列中获取一个项目,并对其进行我需要做的工作。当bw完成它的任务时,它应该调用bw_RunWorkerCompleted,如果队列中有更多的项目,它应该指示它继续在队列中工作。
class DiscQueue
{
private Queue<Disc> myDiscQueue = new Queue<Disc>();
private BackgroundWorker bw = new BackgroundWorker();
// Initializer
public DiscQueue()
{
// Get the background worker setup.
this.bw.WorkerReportsProgress = false;
this.bw.WorkerSupportsCancellation = false;
this.bw.DoWork += new DoWorkEventHandler(bw_DoWork);
}
public void AddToQueue(Disc newDisc)
{
this.myDiscQueue.Enqueue(newDisc);
if (!this.bw.IsBusy)
{
this.bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
DiscPreparationFactory discToPrepare = new DiscPreparationFactory();
Disc currentDisc = new Disc();
currentDisc = this.myDiscQueue.Dequeue();
discToPrepare.PrepareAndPublish(currentDisc);
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.myDiscQueue.Count > 0)
{
this.bw.RunWorkerAsync();
}
}
}
在测试中,我发现快速连续地向队列中添加项目在某种程度上破坏了队列,以便队列中的所有项目都被分配到添加到队列中的最后一个项目的值(可能是对?的引用)。当我在调试中逐步执行代码时,当您在AddToQueue函数中执行if (!this.bw. isbusy)时,似乎发生了这种情况。我不知道发生了什么。
除了回答我的具体问题之外,我确信我是在反向做事,我很高兴知道"正确的方法"。
编辑:这是我的光盘类:
public class Disc
{
public enum DiscFormat
{
Audio,
Data,
}
private string sku;
private int quantity;
private DiscFormat format;
public string Sku
{
get
{
return this.sku;
}
set
{
this.sku = value;
}
}
public DiscFormat Format
{
get
{
return this.format;
}
set
{
this.format = value;
}
}
public int Quantity
{
get
{
return this.quantity;
}
set
{
this.quantity = value;
}
}
}
EDIT 2:我的DiscQueue对象在我的MainForm.cs文件中实例化如下:
public partial class MainForm : Form
{
Disc currentCD = new Disc();
private DiscQueue discQueue = new DiscQueue();
public MainForm()
{
// Do some stuff...
}
// Skipping over a bunch of other methods...
private void buttonSubmit_Click(object sender, EventArgs e)
{
currentCD.Sku = this.textBoxSku.Text;
currentCD.Quantity = (int)this.numericUpDownQuantity.Value;
if (this.radioButtonAudio.Checked)
currentCD.Format = Disc.DiscFormat.Audio;
else
currentCD.Format = Disc.DiscFormat.Data;
this.discQueue.AddToQueue(currentCD);
}
}
标准。net Queue<T>
不是"线程安全的";我特别强调:
一个
Queue<T>
可以支持多个并发读,只要不修改集合。即便如此,在集合中枚举本质上也不是线程安全的过程。为了保证枚举期间的线程安全,可以在整个枚举期间锁定集合。要允许多个线程访问集合进行读写,必须实现自己的同步
如果你有。net 4.0,你应该考虑使用ConcurrentQueue<T>
。
如果没有,您可以使用一个简单的锁来保护DiscQueue
内部的读/写访问:
/// <summary>
/// Synchronizes access to <see cref="DiscQueue.myDiscQueue" />.
/// </summary>
private object queueLock = new object();
那么你的读者会像这样使用锁:
Disc currentDisc = null;
lock (this.queueLock)
{
// protect instance members of Queue<T>
if (this.myDiscQueue.Count > 0)
{
currentDisc = this.myDiscQueue.Dequeue();
}
}
// work with currentDisk
你的写入器会像这样使用锁:
lock (this.queueLock)
{
this.myDiscQueue.Add(currentCD);
}