C# 如何在多个处理器之间分派工作?

C# 如何在多个处理器之间分派工作?,c#,.net,multithreading,asp.net-web-api,C#,.net,Multithreading,Asp.net Web Api,我有一个执行一些数据处理的类: class Processor { public Processor() { // Load lot of data } public string GetResult(string input) { // ... } } 我需要实现一个向这个类公开HTTP API的服务。我使用Owin和Microsoft.AspNet.*libs托管HTTP Web API。对于每个请求,它都会创建一个新线程来

我有一个执行一些数据处理的类:

class Processor
{
    public Processor() {
        // Load lot of data
    }
    public string GetResult(string input) {
        // ... 
    }
}
我需要实现一个向这个类公开HTTP API的服务。我使用Owin和Microsoft.AspNet.*libs托管HTTP Web API。对于每个请求,它都会创建一个新线程来处理它,但我不能在每个请求上实例化
处理器
,因为在其构造函数中加载一些数据需要花费大量时间。此外,我不能重用来自不同线程的一个实例,因为它不是为线程安全而设计的。但我可以在服务启动时实例化
处理器的几个实例,然后在它们之间分派工作。假设我允许最多20个并发HTTP请求用于我的服务。我创建了20个
处理器的实例
,并将忙碌标志添加到类中:

class Processor
{
    public bool Busy { get; set; }
    // ...
}
我写的
Dispatcher
类如下:

class Dispatcher
{
    readonly Processor[] _processors;
    readonly SemaphoreSlim _semaphore;

    public Dispatcher(int maxProcessors)
    {
        _semaphore = new SemaphoreSlim(maxProcessors);
        _processors = new Processor[maxProcessors];
        // Instantiate Processors, etc...
    }

    public string GetResult(string input)
    {
        try
        {
            _semaphore.Wait(); // Surplus requests will wait here.
            Processor processor;
            lock (_processors)
            {
                // It is guaranteed that such processor exists if we entered the semaphore.
                processor = _processors.First(p => !p.Busy);
                processor.Busy = true;
            }
            var result = processor.GetResult(input);
            processor.Busy = false;
            return result;
        }
        finally
        {
            _semaphore.Release();
        }
    }
}
然后我基本上可以通过ApiController中的
Dispatcher
调用它:

public class ServiceController : ApiController
{
    static Dispatcher _dispatcher = new Dispatcher(20);

    [Route("result")]
    [HttpGet]
    public string Result(string input)
    {
        return _dispatcher.GetResult(input);
    }
}
对于我的目的,它是否正确实施?
我对它进行了测试,它可以正常工作,但我想知道我是否重新发明了wheel和.NET Framework,它是否可以在我的案例中使用,或者它是否可以更容易地实现。

基本上,在将在线程中运行的类中,创建一个事件和事件处理程序。然后启动此任务的对象可以注册到该事件。当任务引发事件时(在本例中,当事件完成时,您将引发事件),您可以做一些事情,即,给它更多的工作

在将在子线程中运行的类中创建事件:

公共事件任务已完成,但尚未完成;
公共事件任务ErrorEventHandler OnError

注册到正在旋转类的对象中的事件:

task.OnComplete += TaskComplete;
task.OnError += TaskComplete;
在调用类中创建将处理事件的函数:

public void TaskComplete()
{
//give the thread more work
}

@SLaks在我的例子中,如何使用
BlockingCollection
还不太清楚,因为我不仅需要分发工作项,还需要收集结果。假设我有一定数量的工作人员使用
BlockingCollection
,我应该以某种方式等待相应的结果准备好并获得它。这是可以解决的,但我不确定它是否比这更容易…在我看来,这是一个非常好的实现。我不知道.Net配置会处理您的用例。如果处理器的数量始终等于最大并发请求数,则信号量可能是冗余的(较低级别的信号量已经可以保护处理器池免受饥饿);但是,如果处理器类是CPU绑定的而不是IO绑定的,则可能需要根据可用的处理内核数量调整处理器池大小,这样可以最大限度地减少并发线程之间的上下文切换,并可能获得更好的整体性能。@BlueStrat如果您指的是维护最大并发请求数的某个内部AspNet信号量,那么是的,我故意添加了一个信号量,在服务配置错误的情况下,防止线程安全错误。使用信号量相当低,而我自己也没有使用它们,因为我在C++中编写。我已经做了几个多线程应用程序,但没有使用它们,我从来没有遇到过问题。基本上只是一个循环,只要有一个线程可用,它就会启动一个新线程。我越来越深刻地意识到你误解了这个问题。在我的情况下,我必须有一个单一的入口点,从那里我可以分派工作。此入口点是处理对web服务的特定URL的请求的方法,并且此方法由不同线程(很可能)同时调用。没有任何有限的项目需要处理,我必须尽快处理传入的请求并返回结果。