C#:为迭代正确锁定队列
我使用队列(C#)来存储必须发送到任何连接的客户端的数据 我的lock语句为私有只读:C#:为迭代正确锁定队列,c#,multithreading,locking,queue,synchronized,C#,Multithreading,Locking,Queue,Synchronized,我使用队列(C#)来存储必须发送到任何连接的客户端的数据 我的lock语句为私有只读: private readonly object completedATEQueueSynched = new object(); 只有两种方法正在排队: 1) 由鼠标移动启动,由mainform线程执行: public void handleEddingToolMouseMove(MouseEventArgs e) { AbstractTrafficElement de = new... s
private readonly object completedATEQueueSynched = new object();
只有两种方法正在排队:
1) 由鼠标移动启动,由mainform线程执行:
public void handleEddingToolMouseMove(MouseEventArgs e)
{
AbstractTrafficElement de = new...
sendElementToAllPlayers(de)
lock (completedATEQueueSynched)
{
completedATEQueue.Enqueue(de);
}
}
2) 在按钮事件上启动,也由mainform线程执行(此处无所谓,但安全性优于抱歉):
此方法由负责连接的特定客户端的线程调用。这是:
private void sendSetData(TcpClient c)
{
NetworkStream clientStream = c.GetStream();
lock (completedATEQueueSynched)
{
foreach (AbstractTrafficElement ate in MainForm.completedATEQueue)
{
binaryF.Serialize(clientStream, ate);
}
}
}
如果客户端连接,而我同时移动鼠标,则会发生死锁。
如果我只锁定迭代,将抛出InvalidOperation执行,因为队列已更改
我也尝试过同步队列包装器,但它不适合迭代。(即使与锁结合使用)
有什么想法吗?我只是不明白我的错误看起来像是你想要的
更新
是,工作正常,TryDequeue在Interlocated.CompareExchange和SpinWait中使用。锁不是好的选择,因为太贵了,看一看就别忘了 Her从ConcurrentQueue排队,正如您所看到的,只有
SpinWait
和互锁。使用增量
。看起来很不错
public void Enqueue(T item)
{
SpinWait spinWait = new SpinWait();
while (!this.m_tail.TryAppend(item, ref this.m_tail))
spinWait.SpinOnce();
}
internal void Grow(ref ConcurrentQueue<T>.Segment tail)
{
this.m_next = new ConcurrentQueue<T>.Segment(this.m_index + 1L);
tail = this.m_next;
}
internal bool TryAppend(T value, ref ConcurrentQueue<T>.Segment tail)
{
if (this.m_high >= 31)
return false;
int index = 32;
try
{
}
finally
{
index = Interlocked.Increment(ref this.m_high);
if (index <= 31)
{
this.m_array[index] = value;
this.m_state[index] = 1;
}
if (index == 31)
this.Grow(ref tail);
}
return index <= 31;
}
公共作废排队(T项)
{
SpinWait SpinWait=新的SpinWait();
而(!this.m_tail.TryAppend(item,ref this.m_tail))
spinWait.SpinOnce();
}
内部空隙增长(参考ConcurrentQueue.段尾)
{
this.m_next=新的ConcurrentQueue.Segment(this.m_索引+1L);
tail=this.m_next;
}
内部bool TryAppend(T值,参考ConcurrentQueue.Segment tail)
{
如果(该高度>=31)
返回false;
int指数=32;
尝试
{
}
最后
{
索引=联锁增量(参考此m_高);
如果(index您可以减少争用,可能足以使其可接受:
private void sendSetData(TcpClient c)
{
IEnumerable<AbstractTrafficElement> list;
lock (completedATEQueueSynched)
{
list = MainForm.completedATEQueue.ToList(); // take a snapshot
}
NetworkStream clientStream = c.GetStream();
foreach (AbstractTrafficElement ate in list)
{
binaryF.Serialize(clientStream, ate);
}
}
private void sendSetData(TcpClient c)
{
i可数列表;
锁(completedATEQueueSynched)
{
list=MainForm.completedATEQueue.ToList();//拍摄快照
}
NetworkStream clientStream=c.GetStream();
foreach(列表中的AbstractTrafficElement ate)
{
序列化(clientStream,ate);
}
}
当然,快照引入了它自己的计时逻辑。“所有元素”在任何给定时刻的确切含义是什么?如果你排队、出队的速度不是很高,Henk Holterman的方法是好的。在这里,我想你是捕捉鼠标移动的。如果你希望生成大量数据,我会n队列上述方法不好。锁成为网络代码和排队代码之间的争用。此锁的粒度在整个队列级别
在本例中,我将推荐GSerjo所提到的-ConcurrentQueue。我已经研究了这个队列的实现。它非常精细。它在队列中的单个元素级别上运行。当一个线程退出队列时,其他线程可以不停地并行排队。不知道这是否有效?您的dea具体位置在哪里dlocking?如果不知道谁调用了全局中的什么,就很难提出同步问题是什么。最后一部分是否在新客户端连接时运行?还要记住,从锁内部引发事件是个坏主意-事件是同步的(默认情况下),因此您可以通过事件处理程序在另一个线程中调用锁外的某些代码,然后尝试重新输入您的锁,这意味着您的事件调用永远无法完成-死锁!我不认为您实际上是死锁,只是阻塞(太)长。但是如果(未来版本的)Queue
执行了锁定(此)
?这主要是一个理论上的论点,但你想锁定一些只有你的代码才能访问的东西。私有
是不够的。Iirc foreach实际上并不出列;你打算出列吗?(你通常是这样做的,有一个队列)解列工作正常吗?由于我使用列表来组织连接的客户机,如果我知道我的锁出了什么问题会更好。锁工作正常,TryDequeue在interlocated.compareeexchange和SpinWait.GSerjo中使用,您的建议非常好,尽管获取锁本身并不是一个非常昂贵的操作。@HenkHolterman给出的建议解决了这个问题:锁在一个可能较慢的操作(网络序列化)期间被持有。按照Henk的建议重写代码将减少队列争用,并可能给出预期的结果。:)最后一个问题:当我在迭代时排队时,队列会发生什么情况?事实证明,问题是服务器开始发送新元素(当我移动鼠标时)当他同时发送旧数据时,解列和序列化停止工作。非常感谢concurrentQueue!我还没有读到关于自旋锁的内容,但据我所知,迭代对队列的作用就像迭代开始时存在的一样,对吗?所以迭代时排队的项目不会出现在迭代中?好建议tion!您只需要知道,在网络传输过程中,可能会有更多的元素排队,很难说代码是否正确,但这可能需要解决=)@sgorozco-这就是我在最后一段中暗示的。它似乎只与Enqueue和GetEnumerator有关,这意味着添加的速率不能达到be非常高。@HenkHolterman我没有收到你的评论。我说的“添加速率”是指调用方每秒调用排队的次数。如果这个速率很高,系统的线程争用率就会上升。
private void sendSetData(TcpClient c)
{
IEnumerable<AbstractTrafficElement> list;
lock (completedATEQueueSynched)
{
list = MainForm.completedATEQueue.ToList(); // take a snapshot
}
NetworkStream clientStream = c.GetStream();
foreach (AbstractTrafficElement ate in list)
{
binaryF.Serialize(clientStream, ate);
}
}