我在两个线程之间使用ConcurrentQueue吗?我想检查一下我不需要明确地“锁定”任何地方。特别是看看我在COMMENTS中的行,我会在这里丢弃一个数据包......
public class PacketCapturer
{
private static ConcurrentQueue<Packet> _packetQueue = new ConcurrentQueue<Packet>();
public PacketCapturer(IPHostEntry proxyDns, ref BackgroundWorker bw)
{
// start the background thread
var backgroundThread = new System.Threading.Thread(BackgroundThread);
backgroundThread.Start();
// Start Packet Capture with callback via PacketCapturerCallback
}
private void PacketCapturerCallback(Packet packet)
{
_packetQueue.Enqueue(packet);
}
private static void BackgroundThread()
{
while (!BackgroundThreadStop)
{
if (_packetQueue.Count == 0) Thread.Sleep(250);
else
{
ConcurrentQueue<Packet> ourQueue;
ourQueue = _packetQueue; // COULD I DROP A PACKET BETWEEN HERE
_packetQueue = new ConcurrentQueue<Packet>(); // AND HERE???
Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count);
}
}
}
答案 0 :(得分:2)
不,不行。首先,如果在并发线程中更改这样的引用,则_packetQueue 必须标记为volatile,以防止编译器和代码生成优化永远不会看到更改。 _packetQueue shoudl的更改通常作为Interlocked.CompareExchange出现,但这对您的使用不太重要。
但更重要的是在后台线程中更改packetQueue实例的模式。这样做的目的是什么?它有一种可怕的代码味道......
<强>更新强>
我通常做的是:
生产者线程:
Producer () {
...
lock(_sharedQueue) {
_sharedQueue.Enqueue(something);
}
...
}
消费者主题:
consumer (...) {
...
var Something[] toProcess = null;
lock(_sharedQueue)
{
toProcess = _sharedQueue.Toarray();
_sharedQueue.Clear();
}
// Process here the toProcess array
...
}
这对我曾经拥有的每一次使用都足够好。在锁定下不会发生处理,因此锁定很小。没有必要使用花哨的ConcurrentQueue,一个普通的.Net 2.0集合就足够了。通常我会使用专用的锁定对象,而不是锁定实际的队列实例。
答案 1 :(得分:1)
编辑:我认为Mark / Gabe是对的,你不会丢失任何数据包。我将其余部分留作参考,以防其他任何人可以权衡这一点。
简单地说,是的。你可能会丢失一个或多个数据包。您可能想要查看ConcurrentQueue提供的方法来获取/删除它的一部分,因为它看起来就像您想要做的那样。
为什么不TryDequeue直到它返回false:
else
{
Queue<Packet> ourQueue = new Queue<Packet>(); //this doesn't need to be concurrent unless you want it to be for some other reason.
Packet p;
while(_packetQueue.TryDequeue(out p))
{
ourQueue.Enqueue(p);
}
Console.WriteLine("BackgroundThread: ourQueue.Count is {0}", ourQueue.Count);
}
答案 2 :(得分:1)
我认为您不能删除数据包,因为显然任何线程都会取消引用 旧的或新的。边缘情况是,在您认为已经交换了之后,您会在ourQueue
中获得更多数据,或者您最终也没有注意到{{1}中的参考更改} loop(但是:有2个线程,唯一更改引用的线程是这个线程,所以不一定是个问题)。
TBH,如果此处有2个主题,为什么不只是Sleep
或使用lock
?弄清楚......会发生什么事情会简单得多。