在启用了超线程的Windows7 quadcore上,我有一百个线程通过BlockingCollection<T>
进行通信(所有线程都使用默认构造函数初始化,因此在内部使用ConcurrentQueue<T>
。
所有主题每秒收到10-100封邮件,但每天只收到4-20条消息 。
我的问题与最后一个消费者有关:大部分时间,它被阻止等待新消息,但是当消息准备好被消费时,应该尽快处理它,可能是即时的。
问题在于,当消息被添加到专用于此消费者的BlockingCollection
时,会在几秒钟后收到消息(Take()在消息入队后3到12秒后返回)。 / p>
我猜这个问题与windows如何安排这个帖子有关。
我试图在没有任何改进的情况下增加此消费者的ThreadPriority。 然后我尝试将其处理器亲和性设置为专用核心,并且我已经改变了所有其他线程的亲和力以使用其他核心,但仍然没有任何改进。
我该如何解决这个问题?什么是实际问题?
这是线程循环(commands
是BlockingCollection
):
while (!commands.IsCompleted)
{
_log.Debug("Waiting for command.");
command = commands.Take();
_log.DebugFormat("Received: {0}", command);
command.ApplyTo(target);
_log.InfoFormat("Dispatched: {0}", command);
}
我应该直接使用ConcurrentQueue
,在没有待处理的消息时,睡眠时间为50毫秒(这是可接受的延迟)吗?
请注意
没有CPU使用超过50%。
至于延迟:日志显示(大多数时间)在“Dispatched:...”和“Waiting for command”之间经过几秒钟。 (又名commands.IsCompleted
)和其他一些“等待命令”。和“收到:......”(又名commands.Take()
)
所有“理智”的线程都是I / O绑定但他们合作:相信我,我无法改变他们的设计。然而它们的效果相当不错:唯一不足的是低负载线程,它可以完成不同类型的工作。我知道thread priority are evil,但我找不到更好的解决方案。
(几乎)复制问题的测试
以下是在服务器上几乎复制问题的测试。 “几乎”因为测试压力CPU(全部达到100%),而在实际应用中所有CPU都低于50%:
public class ThreadTest
{
class TimedInt
{
public int Value;
public DateTime Time;
}
[Test]
public void BlockingCollection_consumedWithLowLoad_delaySomeTimes()
{
// arrange:
int toComplete = 0;
BlockingCollection<KeyValuePair<DateTime, TimedInt>> results = new BlockingCollection<KeyValuePair<DateTime, TimedInt>>();
BlockingCollection<TimedInt> queue = new BlockingCollection<TimedInt>();
Action<int> producer = a =>
{
int i = 1;
int x = Convert.ToInt32(Math.Pow(a, 7));
while (i < 200000000)
{
if (i % x == 0)
{
queue.Add(new TimedInt { Time = DateTime.Now, Value = i });
Thread.SpinWait(100); // just to simulate a bit of actual work here
queue.Add(new TimedInt { Time = DateTime.Now, Value = i + 1 });
}
i++;
}
Interlocked.Decrement(ref toComplete);
};
Action consumer = () =>
{
Thread.CurrentThread.Priority = ThreadPriority.Highest; // Note that the consumer has an higher priority
Thread.CurrentThread.Name = "Consumer";
while (toComplete > 0)
{
TimedInt v;
if (queue.TryTake(out v, 1000))
{
DateTime now = DateTime.Now;
results.Add(new KeyValuePair<DateTime, TimedInt>(now, v));
}
}
};
// act:
List<Thread> threads = new List<Thread>();
threads.Add(new Thread(new ThreadStart(consumer)));
for (int i = 0; i < 200; i++)
{
var t = new Thread(new ThreadStart(() => producer(7 + (i % 3))));
t.Name = "Producer " + i.ToString();
threads.Add(t);
toComplete++;
}
threads.ForEach(t => t.Start());
threads.ForEach(t => t.Join());
// assert:
Assert.AreEqual(0, results.Where(kvp => (kvp.Key - kvp.Value.Time).TotalMilliseconds > 1000).Count());
}
}
有什么想法吗?