C# ThreadPool RegisterWaitForSingleObject的资源使用情况

C# ThreadPool RegisterWaitForSingleObject的资源使用情况,c#,.net,multithreading,resources,threadpool,C#,.net,Multithreading,Resources,Threadpool,我正在编写一个服务器应用程序,它处理来自多个客户端的请求。对于请求的处理,我使用线程池 其中一些请求会修改数据库记录,我想将对该特定记录的访问限制为一次只能访问一个线程池线程。为此,我使用命名信号量(其他进程也在访问这些记录)。 对于每个想要修改记录的新请求,线程应该排队等待轮到它 这就是问题所在: 由于我不希望线程池中充满等待访问记录的线程,我在线程池中找到了RegisterWaitForSingleObject方法。 但是,当我阅读“备注”部分下的文档()时: 在需要时自动创建新的等待线程

我正在编写一个服务器应用程序,它处理来自多个客户端的请求。对于请求的处理,我使用线程池

其中一些请求会修改数据库记录,我想将对该特定记录的访问限制为一次只能访问一个线程池线程。为此,我使用命名信号量(其他进程也在访问这些记录)。
对于每个想要修改记录的新请求,线程应该排队等待轮到它

这就是问题所在:
由于我不希望线程池中充满等待访问记录的线程,我在线程池中找到了RegisterWaitForSingleObject方法。
但是,当我阅读“备注”部分下的文档()时:

在需要时自动创建新的等待线程

这是否意味着线程池将充满等待线程?这会如何影响线程池的性能

任何其他提高性能的建议都是非常受欢迎的


谢谢

IMHO您应该让数据库自己进行同步。你所需要做的就是确保你的进程同步


联锁类可能是一个过早的优化,过于复杂而无法实现。我建议使用更高级的同步对象,如ReaderWriterLockSlim。或者最好是一个监视器。

您的解决方案是一个可行的选择。由于缺乏更具体的细节,我认为我无法提供其他具体的选择。然而,让我试着解释一下,为什么我认为您当前的解决方案至少是基于合理的理论

假设您同时收到64个请求。可以合理地假设线程池可以立即将这些请求中的每一个发送到线程。因此,您可能有64个线程立即开始处理。现在让我们假设互斥已经被另一个线程获取,并且它被保存了很长时间。这意味着这64个线程将被阻塞很长时间,等待当前拥有互斥锁的线程释放互斥锁。这意味着这64个线程被浪费在无所事事上

另一方面,如果您选择使用
RegisterWaitForSingleObject
,而不是使用阻塞调用来等待释放互斥体,那么您可以立即释放这64个等待的线程(工作项),并允许将它们放回池中。如果我要实现自己版本的
RegisterWaitForSingleObject
,那么我将使用
WaitHandle.WaitAny
方法,该方法允许我在单个阻塞方法调用中指定多达64个句柄(我并没有随机选择64个请求数)。我不是说这很容易,但我可以用池中的一个线程替换64个等待线程。我不知道微软是如何实现RegisterWaitForSingleObject方法的,但我猜他们的实现方式至少和我的策略一样有效。换句话说,通过使用
RegisterWaitForSingleObject
,您应该能够将线程池中挂起的工作项的数量至少减少64倍


你看,你的解决方案是基于健全的理论。我并不是说您的解决方案是最优的,但我确实相信您的担心对于所问的特定问题是毫无根据的。

我以前使用过的解决此问题的方法是让获取其中一个工作项的第一个线程负责处理该工作项时发生的任何其他线程,这是通过将工作项排队,然后放入关键部分来处理队列来完成的。只有“第一个”线程会掉入临界段。如果一个线程不能获得临界区,它将离开并让已经在临界区中运行的线程处理排队的对象

这真的不是很复杂-唯一可能不明显的是,当离开关键部分时,处理线程必须以一种不会在队列中留下延迟到达的工作项的方式来完成。基本上,“处理”临界段锁必须在保持队列锁的同时释放。如果不满足这一要求,同步队列就足够了,代码也会非常简单

伪代码:

// `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); 
    }
}
/`workitem`是包含数据库修改请求的对象
//
//'queue'是一个可以容纳这些工作项请求的队列
//
//'processing_lock'是用于提供锁的对象
//指示线程正在处理队列
//任何数量的线程都可以调用此函数,但只能调用一个
//将最终处理所有工作项。
//
//其他线程只需将工作项放到队列中
//然后离开
void threadpoolHandleDatabaseUpdateRequest(工作项)
{
//将工作项置于队列中
Monitor.Enter(queue.SyncRoot);
queue.Enqueue(工作项);
Monitor.Exit(queue.SyncRoot);
布尔处理;
Monitor.TryEnter(处理队列,数据处理);
if(!doProcessing){
//另一个线程具有处理锁,它将
//处理工作项目
返回;
}
对于(;;){
Monitor.Enter(queue.SyncRoot);
if(queue.Count()==0){
//已完成对队列的处理
//按确保安全的顺序释放锁
//工作项不会被困在队列中
监视器。退出(处理队列);
Monitor.Exit(queue.SyncRoot);
打破
}
workitem=queue.Dequeue();
Monitor.Exit(queue.SyncRoot);
//这将获得数据库互斥锁,进行更新并发布
//数据库互斥
doDatabaseModification(工作项);
}
}

线程池为大约64个可等待对象创建等待线程

这里有好的评论:

RW