我正在编写一个服务器应用程序,它处理来自多个客户端的请求。在处理请求时,我使用线程池。
其中一些请求修改了数据库记录,我希望将对该特定记录的访问限制为一次一个线程池线程。为此,我使用了命名信号量(其他进程也在访问这些记录)。
对于每一个想要修改记录的新请求,线程都应该排队等待。
这就是问题所在:
由于我不希望线程池被等待访问记录的线程填满,所以我在线程池中找到了RegisterWaitForSingleObject方法。
但是,当我阅读文档(MSDN)一节中的备注时:
新的等待线程将在需要时自动创建。..。
这是否意味着线程池将被等待线程填满?以及这如何影响线程池的性能?
任何其他的建议,以提高性能是非常欢迎的!
谢谢!
发布于 2011-04-16 03:44:14
你的解决方案是一个可行的选择。在没有更具体的细节的情况下,我认为我无法提供其他具体的选择。然而,让我试图说明为什么我认为你目前的解决方案是,至少,基于健全的理论。
假设您同时收到了64个请求。合理的假设是线程池可以立即将每个请求分派给线程。因此,您可能有64个线程立即开始处理。现在让我们假设互斥对象已经被另一个线程获取了,并且它被持有了很长的时间。这意味着这64个线程将被阻塞很长时间,等待当前拥有互斥锁的线程释放它。这意味着这64个线程被浪费在不做任何事情上。
另一方面,如果您选择使用RegisterWaitForSingleObject
而不是使用阻塞调用来等待互斥锁的释放,那么您可以立即释放这64个等待线程(工作项),并允许它们返回池。如果我要实现我自己版本的RegisterWaitForSingleObject
,那么我将使用WaitHandle.WaitAny
方法,它允许我在单个阻塞方法调用中指定多达64个句柄(毕竟不是随机选择64个请求数量)。我并不是说这会很容易,但我可以将我的64个等待线程替换为池中的一个线程。我不知道微软是如何实现RegisterWaitForSingleObject
方法的,但我猜他们这样做的方式至少和我的策略一样有效。换句话说,通过使用RegisterWaitForSingleObject
,您应该能够将线程池中挂起的工作项的数量减少至少64倍。
所以你看,你的解决方案是基于健全的理论。我并不是说你的解决办法是最佳的,但我确实认为你对所提出的具体问题的关切是没有道理的。
发布于 2011-04-16 02:43:57
IMHO您应该让数据库进行自己的同步。您所需要做的就是确保在您的进程中同步。
互锁类可能是一个过早的优化,过于复杂,无法实现。我建议使用更高级的同步对象,如ReaderWriterLockSlim。或者更好的是监视器。
发布于 2011-04-16 17:05:40
我以前使用过的解决此问题的一种方法是让获得这些工作项之一的第一个线程负责处理工作项时发生的任何其他线程,这是通过排队工作项,然后进入关键部分来处理队列来完成的。只有“第一个”线程才会进入关键部分。如果线程无法获得关键部分,它将离开,并让已经在关键部分中操作的线程处理排队对象。
这并不是很复杂--唯一可能不明显的是,当离开关键部分时,处理线程必须以一种不可能将到达晚的工作项留在队列中的方式来完成它。基本上,“处理”关键部分锁必须在保持队列锁时释放。如果没有这一项要求,同步队列就足够了,代码将非常简单!
伪码:
// `workitem` is an object that contains the database modification request
//
// `queue` is a Queue<T> that can hold these workitem requests
//
// `processing_lock` is an object use to provide a lock
// to indicate a thread is processing the queue
// any number of threads can call this function, but only one
// will end up processing all the workitems.
//
// The other threads will simply drop the workitem in the queue
// and leave
void threadpoolHandleDatabaseUpdateRequest(workitem)
{
// put the workitem on a queue
Monitor.Enter(queue.SyncRoot);
queue.Enqueue(workitem);
Monitor.Exit(queue.SyncRoot);
bool doProcessing;
Monitor.TryEnter(processing_queue, doProcessing);
if (!doProcessing) {
// another thread has the processing lock, it'll
// handle the workitem
return;
}
for (;;) {
Monitor.Enter(queue.SyncRoot);
if (queue.Count() == 0) {
// done processing the queue
// release locks in an order that ensures
// a workitem won't get stranded on the queue
Monitor.Exit(processing_queue);
Monitor.Exit(queue.SyncRoot);
break;
}
workitem = queue.Dequeue();
Monitor.Exit(queue.SyncRoot);
// this will get the database mutex, do the update and release
// the database mutex
doDatabaseModification(workitem);
}
}
https://stackoverflow.com/questions/5684087
复制相似问题