【发布时间】:2017-03-30 12:10:26
【问题描述】:
我有一个MessagesManager 线程,不同的线程可以向它发送消息,然后这个MessagesManager 线程负责在SendMessageToTcpIP() 中发布这些消息(MessagesManager 线程的起点)。
class MessagesManager : IMessageNotifier
{
//private
private readonly AutoResetEvent _waitTillMessageQueueEmptyARE = new AutoResetEvent(false);
private ConcurrentQueue<string> MessagesQueue = new ConcurrentQueue<string>();
public void PublishMessage(string Message)
{
MessagesQueue.Enqueue(Message);
_waitTillMessageQueueEmptyARE.Set();
}
public void SendMessageToTcpIP()
{
//keep waiting till a new message comes
while (MessagesQueue.Count() == 0)
{
_waitTillMessageQueueEmptyARE.WaitOne();
}
//Copy the Concurrent Queue into a local queue - keep dequeuing the item once it is inserts into the local Queue
Queue<string> localMessagesQueue = new Queue<string>();
while (!MessagesQueue.IsEmpty)
{
string message;
bool isRemoved = MessagesQueue.TryDequeue(out message);
if (isRemoved)
localMessagesQueue.Enqueue(message);
}
//Use the Local Queue for further processing
while (localMessagesQueue.Count() != 0)
{
TcpIpMessageSenderClient.ConnectAndSendMessage(localMessagesQueue.Dequeue().PadRight(80, ' '));
Thread.Sleep(2000);
}
}
}
不同的线程 (3-4) 通过调用 PublishMessage(string Message) 发送它们的消息(使用相同的对象到 MessageManager)。消息到达后,我将该消息推送到并发队列,并通过设置_waitTillMessageQueueEmptyARE.Set(); 通知SendMessageToTcpIP()。 在SendMessageToTcpIP()里面,我是从本地队列里面的并发队列中复制消息,然后一一发布。
问题:以这种方式进行入队和出队是否线程安全?会不会有什么奇怪的效果?
【问题讨论】:
-
为什么要检查计数或使用 AutoResetEvent?为什么是本地队列? ConcurrentQueue 是线程安全的,不需要任何代码。如果您想遍历现有消息,可以使用
GetConsumingEnumerable()。该代码只能引入线程安全问题 -
因为您的消息(
string类型)是不可变的,这应该是安全的。但是,如果您的消息是可变的,那么即使队列本身是线程安全的,您的使用也不一定是线程安全的。例如,如果某个其他线程在另一个线程使用它时改变了一条消息。 -
我正在使用
AutoResetEvent,因为MessagesManager线程正在调用SendMessageToTcpIP(),它应该保持等待状态,直到有新消息到来。 -
你如何重新启动SendMessageToTcpIP?似乎它只会运行一次(没有全局 while 循环)。
-
@skm 它会,即使你不使用它。使消息出队是一种阻塞操作。一个简单的
public void SendMessageToTcpIP(){ foreach(var message in MessageQueue.GetConsumingEnumerable(){TcpIpMessageSenderClient.ConnectAndSendMessage(message.PadRight(80, ' ');}}就足够了
标签: c# multithreading