我正在编写一个处理来自多个客户端的请求的服务器应用程序。对于处理请求,我使用 threadpool 。
其中一些请求会修改数据库记录,我希望一次将对该特定记录的访问限制为一个 threadpool 线程。为此,我使用命名的信号量(其他进程也访问这些记录) 对于每个想要修改记录的新请求,线程应该排队等待。
这就是问题的来源:
由于我不希望线程池填满等待访问记录的线程,我在 threadpool 中找到了 RegisterWaitForSingleObject 方法。
但是,当我阅读备注部分下的文档(MSDN)时:
需要时会自动创建新的等待线程。 ...
这是否意味着 threadpool 将填满等待线程?这如何影响线程池的性能?
欢迎任何其他提高性能的建议!
谢谢!
答案 0 :(得分:0)
恕我直言,您应该让数据库自己进行同步。您需要做的就是确保您在流程中同步。
互锁类可能是一个过于复杂而不能实现的过早优化。我建议使用更高级别的同步对象,例如ReaderWriterLockSlim。或者更好的是,监视器。
答案 1 :(得分:0)
您的解决方案是可行的选择。在没有更具体的细节的情况下,我认为我不能提供其他有形的选择。但是,让我试着说明为什么我认为你目前的解决方案至少是基于合理的理论。
假设您有64个请求同时进入。假设线程池可以立即将这些请求中的每一个发送到线程是合理的。所以你可能有64个线程立即开始处理。现在让我们假设互斥锁已经被另一个线程获取并且它被保持了很长时间。这意味着那些64个线程将被长时间阻塞,等待当前拥有互斥锁的线程释放它。这意味着那64个线程无所事事就浪费了。
另一方面,如果您选择使用RegisterWaitForSingleObject
而不是使用阻塞调用来等待互斥锁被释放,那么您可以立即释放那64个等待线程(工作项)并允许它们被放回游泳池。如果我要实现我自己的RegisterWaitForSingleObject
版本,那么我会使用WaitHandle.WaitAny
方法,它允许我指定最多64个句柄(我没有随机选择64个作为请求的数量)单阻塞方法调用。我并不是说这很容易,但我可以替换我的64个等待线程只从池中的一个线程。我不知道Microsoft如何实现RegisterWaitForSingleObject
方法,但我猜他们是以一种至少和我的策略一样有效的方式来实现的。换句话说,您应该能够使用RegisterWaitForSingleObject
将线程池中待处理工作项的数量减少至少64倍。
所以你看,你的解决方案是基于合理的理论。我并不是说你的解决方案是最优的,但我相信你对所提出的具体问题的关注是没有根据的。
答案 2 :(得分:0)
我之前使用的一个解决这个问题的方法是让第一个获得这些工作项之一的线程负责处理工作项时发生的任何其他工作,这是通过排队完成的然后,工作项将进入关键部分以处理队列。只有'第一个'线程才会进入临界区。如果某个线程无法获取临界区,它将离开并让已经在临界区运行的线程处理排队对象。
这真的不是很复杂 - 唯一可能不明显的是,当离开临界区时,处理线程必须以不会在队列中留下迟到的工作项的方式进行。基本上,必须在保持队列锁定的同时释放“处理”关键部分锁。如果不是这个要求,同步队列就足够了,代码真的很简单!
伪代码:
// `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);
}
}
答案 3 :(得分:0)
ThreadPool为~64个可等待的对象创建一个等待线程。