我最近开始了一项新工作,这里有一个Windows服务,它使用来自私有Windows队列的消息。此服务仅在上午9点至下午6点消耗消息。因此,在晚上7点到8点59分期间,它会在队列中累积大量消息。当它在晚上9点开始处理时,服务的CPU使用率会很高(98%,99%),这会影响服务器的性能。
这个服务使用线程来处理队列的消息,但是在我有点迷失之前我从未使用过线程。
以下是我确信发生这种情况的代码部分:
private Thread[] th;
//in the constructor of the class, the variable th is initialized like this:
this.th = new Thread[4];
//the interval of this method calling is 1sec, it only goes high cpu usage when there is a lot of messages in the queue
public void Exec()
{
try
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
int vQtd = queue.GetAllMessages().Length;
while (vQtd > 0)
{
for (int y = 0; y < th.Length; y++)
{
if (this.th[y] == null || !this.th[y].IsAlive)
{
this.th[y] = new Thread(new ParameterizedThreadStart(ProcessMessage));
this.th[y].Name = string.Format("Thread_{0}", y);
this.th[y].Start(new Controller(queue.Receive(), autoEvent));
vQtd--;
}
}
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "RECOVERABLE");
}
}
编辑:我正在尝试Brian Gideon发布的第二种方法。但老实说:我对代码非常困惑,而且我对它正在做的事情一无所知。
我没有改变创建4个线程的方式和我展示的其他代码,只是改变了我的Exec(exec是在每天上午9点到下午6点时调用的方法)方法:
public void Exec()
{
try
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
int vQtd = queue.GetAllMessages().Length;
while (vQtd > 0)
{
for (int i = 0; i < 4; i++)
{
var thread = new Thread(
(ProcessMessage) =>
{
while (true)
{
Message message = queue.Receive();
Controller controller = new Controller(message, autoEvent);
//what am I supposed to do with the controller?
}
});
thread.IsBackground = true;
thread.Start();
}
vQtd--;
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "RECOVERABLE");
}
}
答案 0 :(得分:2)
哎哟。我必须诚实。这不是一个很好的设计。它很可能围绕while
循环旋转,等待先前的线程完成处理。这是一种更好的方法。请注意,4个线程只创建一次并永远挂起。下面的代码使用.NET 4.0 BCL中的BlockingCollection。如果您使用的是早期版本,则可以将其替换为Stephen Toub的BlockingQueue。
注意:在您的情况下可能需要进一步重构。此代码尝试保留原始元素中的一些常用元素。
public class Example
{
private BlockingCollection<Controller> m_Queue = new BlockingCollection<Controller>();
public Example()
{
for (int i = 0; i < 4; i++)
{
var thread = new Thread(
() =>
{
while (true)
{
Controller controller = m_Queue.Take();
// Do whatever you need to with Contoller here.
}
});
thread.IsBackground = true;
thread.Start();
}
}
public void Exec()
{
try
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
int vQtd = Queue.GetAllMessages().Length
while (vQtd > 0)
{
m_Queue.Add(new Controller(Queue.Receive(), autoEvent));
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "RECOVERABLE");
}
}
}
修改强>
或者更好,因为MessageQueue
是线程安全的:
public class Example
{
public Example()
{
for (int i = 0; i < 4; i++)
{
var thread = new Thread(
() =>
{
while (true)
{
if (/* between 9am and 6pm */)
{
Message message = queue.Receive();
Controller controller = new Controller(message, /* AutoResetEvent? */);
// Do whatever you need to with Contoller here.
// Is the AutoResetEvent really needed?
}
}
});
thread.IsBackground = true;
thread.Start();
}
}
}
答案 1 :(得分:0)
您有两种选择。您可以在每个带有Thread.Sleep()
的消息之后插入延迟,或者降低轮询线程的线程优先级。如果降低线程优先级,CPU使用率仍然很高,但不应该影响性能。
编辑:或者您可以将线程数从4减少到3以留下一个核心用于其他处理(假设您有四核)。这当然会降低您的出列吞吐量。
Edit2:或者,如果您运行的是.NET,则可以使用task parallel library重写整个思考。查找Parallel.ForEach()。如果您不熟悉线程,这应该可以避免一些步法。
答案 2 :(得分:0)
当所有线程都忙时,您显示的方法在紧密循环中运行。尝试这样的事情:
while (vQtd > 0)
{
bool full = true;
for (int y = 0; y < th.Length; y++)
{
if (this.th[y] == null || !this.th[y].IsAlive)
{
this.th[y] = new Thread(new ParameterizedThreadStart(ProcessMessage));
this.th[y].Name = string.Format("Thread_{0}", y);
this.th[y].Start(new Controller(queue.Receive(), autoEvent));
vQtd--;
full = false;
}
}
if (full)
{
Thread.Sleep(500); // Or whatever it may take for a thread to become free.
}
}