我正在.net中构建一个多线程应用程序。
我有一个监听连接的线程(abstract,serial,tcp ...)。
当收到新消息时,它会通过AddMessage将其添加到消息中。然后调用startSpool。 startSpool检查假脱机是否已经运行,如果是,则返回,否则,在新线程中启动它。原因是,消息必须按顺序处理,FIFO。
所以,我的问题是...... 我是以正确的方式来做这件事的吗? 那里有更好,更快,更便宜的模式吗?
如果我的代码中有拼写错误,我很抱歉,我在复制和粘贴方面遇到了问题。
ConcurrentQueue<IMyMessage > messages = new ConcurrentQueue<IMyMessage>();
const int maxSpoolInstances = 1;
object lcurrentSpoolInstances;
int currentSpoolInstances = 0;
Thread spoolThread;
public void AddMessage(IMyMessage message)
{
this.messages.Add(message);
this.startSpool();
}
private void startSpool()
{
bool run = false;
lock (lcurrentSpoolInstances)
{
if (currentSpoolInstances <= maxSpoolInstances)
{
this.currentSpoolInstances++;
run = true;
}
else
{
return;
}
}
if (run)
{
this.spoolThread = new Thread(new ThreadStart(spool));
this.spoolThread.Start();
}
}
private void spool()
{
Message.ITimingMessage message;
while (this.messages.Count > 0)
{
// TODO: Is this below line necessary or does the TryDequeue cover this?
message = null;
this.messages.TryDequeue(out message);
if (message != null)
{
// My long running thing that does something with this message.
}
}
lock (lcurrentSpoolInstances)
{
this.currentSpoolInstances--;
}
}
答案 0 :(得分:4)
使用BlockingCollection<T>
代替ConcurrentQueue<T>
会更容易。
这样的事情应该有效:
class MessageProcessor : IDisposable
{
BlockingCollection<IMyMessage> messages = new BlockingCollection<IMyMessage>();
public MessageProcessor()
{
// Move this to constructor to prevent race condition in existing code (you could start multiple threads...
Task.Factory.StartNew(this.spool, TaskCreationOptions.LongRunning);
}
public void AddMessage(IMyMessage message)
{
this.messages.Add(message);
}
private void Spool()
{
foreach(IMyMessage message in this.messages.GetConsumingEnumerable())
{
// long running thing that does something with this message.
}
}
public void FinishProcessing()
{
// This will tell the spooling you're done adding, so it shuts down
this.messages.CompleteAdding();
}
void IDisposable.Dispose()
{
this.FinishProcessing();
}
}
编辑:如果您想支持多个消费者,您可以通过单独的构造函数来处理它。我将其重构为:
public MessageProcessor(int numberOfConsumers = 1)
{
for (int i=0;i<numberOfConsumers;++i)
StartConsumer();
}
private void StartConsumer()
{
// Move this to constructor to prevent race condition in existing code (you could start multiple threads...
Task.Factory.StartNew(this.spool, TaskCreationOptions.LongRunning);
}
这将允许您启动任意数量的消费者。请注意,这会破坏严格使用FIFO的规则 - 处理可能会在此更改的情况下处理块中的“numberOfConsumer”元素。
已支持多个制作者。以上是线程安全的,因此任意数量的线程都可以并行调用Add(message)
,而不做任何更改。
答案 1 :(得分:1)
我认为Reed的答案是最好的方法,但是为了学术,这里有一个使用并发队列的例子 - 你发布的代码中有一些比赛(取决于你如何处理增加currnetSpoolInstances )
我所做的更改(如下)是:
private ConcurrentQueue<IMyMessage> messages = new ConcurrentQueue<IMyMessage>();
const int maxSpoolInstances = 1;
object lcurrentSpoolInstances = new object();
int currentSpoolInstances = 0;
public void AddMessage(IMyMessage message)
{
this.messages.Enqueue(message);
this.startSpool();
}
private void startSpool()
{
lock (lcurrentSpoolInstances)
{
if (currentSpoolInstances < maxSpoolInstances)
{
this.currentSpoolInstances++;
Task.Factory.StartNew(spool, TaskCreationOptions.LongRunning);
}
}
}
private void spool()
{
IMyMessage message;
while (true)
{
// you do not need to null message because it is an "out" parameter, had it been a "ref" parameter, you would want to null it.
if(this.messages.TryDequeue(out message))
{
// My long running thing that does something with this message.
}
else
{
lock (lcurrentSpoolInstances)
{
if (this.messages.IsEmpty)
{
this.currentSpoolInstances--;
return;
}
}
}
}
}
答案 2 :(得分:0)
检查&#39;管道模式&#39;:http://msdn.microsoft.com/en-us/library/ff963548.aspx
HTH ..