“固定”/“负载均衡”C#线程池?

时间:2016-02-10 02:04:36

标签: c# multithreading wcf

我有一个“昂贵”的第三方组件。该组件不是线程安全的。所述组件托管在WCF服务中(暂时),因此......每次调用进入服务时,我都必须新建组件。

我想要做的是拥有一个说16个线程的池,每个线程都会启动它们自己的组件副本,并且有一个机制来调用该方法并将其分配给16个线程中的一个并拥有返回值。

如此简单:

var response = threadPool.CallMethod(param1, param2);

对于阻止调用它很好,直到它得到响应,因为我需要响应才能继续。

有什么建议吗?也许我正在过度思考它并且由16个线程提供服务的ConcurrentQueue可以完成这项工作,但是现在确定方法返回值将如何返回给调用者?

2 个答案:

答案 0 :(得分:2)

WCF已经使用线程池来管理它的资源,所以如果你在它之上添加一层线程管理,它只会变得非常糟糕。如果可能的话,请避免这样做,因为您会在服务电话上争用。

在您的情况下我会做的只是使用 ThreadLocal或线程静态,这将使用昂贵的对象初始化一次。此后它将可用于线程池线程。

假设你的对象在MTA线程上没问题;我猜这是来自你的帖子,因为它听起来像当前工作,但只是很慢。

有人担心会创建太多的对象,并且当池变得太大时会占用太多内存。但是,在执行任何其他操作之前,请查看实际情况是否如此。这是一个非常简单的策略,可以轻松实现。如果你真的需要,只会变得更加复杂。

答案 1 :(得分:2)

首先,我同意@briantyler:ThreadLocal<T>或线程静态字段可能就是你想要的。如果它不能满足您的需求,您应该将其作为起点并考虑其他选项。

复杂但灵活的替代方案是单例对象池。在最简单的形式中,您的池类型将如下所示:

public sealed class ObjectPool<T>
{
    private readonly ConcurrentQueue<T> __objects = new ConcurrentQueue<T>();
    private readonly Func<T> __factory;

    public ObjectPool(Func<T> factory)
    {
        __factory = factory;
    }

    public T Get()
    {
        T obj;
        return __objects.TryDequeue(out obj) ? obj : __factory();
    }

    public void Return(T obj)
    {
        __objects.Enqueue(obj);
    }
}

如果您在原始类或结构(即T)方面考虑类型ObjectPool<MyComponent>,这似乎并不是非常有用,因为池没有任何线程内置控件。但您可以将T类型替换为Lazy<T>Task<T> monad,并获得您想要的内容。

池初始化:

Func<Task<MyComponent>> factory = () => Task.Run(() => new MyComponent());
ObjectPool<Task<MyComponent>> pool = new ObjectPool<Task<MyComponent>>(factory);

// "Pre-warm up" the pool with 16 concurrent tasks.
// This starts the tasks on the thread pool and
// returns immediately without blocking.
for (int i = 0; i < 16; i++) {
    pool.Return(pool.Get());
}

用法:

// Get a pooled task or create a new one. The task may
// have already completed, in which case Result will
// be available immediately. If the task is still
// in flight, accessing its Result will block.
Task<MyComponent> task = pool.Get();

try
{
    MyComponent component = task.Result; // Alternatively you can "await task"

    // Do something with component.
}
finally
{
    pool.Return(task);
}

此方法比在ThreadLocal或线程静态字段中维护组件更复杂,但如果您需要做一些像限制池化实例数量这样的事情,那么池抽象可能非常有用。

修改

基本&#34;固定的X实例集&#34;使用Get的池实现,一旦池被耗尽就会阻塞:

public sealed class ObjectPool<T>
{
    private readonly Queue<T> __objects;

    public ObjectPool(IEnumerable<T> items)
    {
        __objects = new Queue<T>(items);
    }

    public T Get()
    {
        lock (__objects)
        {
            while (__objects.Count == 0) {
                Monitor.Wait(__objects);
            }

            return __objects.Dequeue();
        }
    }

    public void Return(T obj)
    {
        lock (__objects)
        {
            __objects.Enqueue(obj);

            Monitor.Pulse(__objects);
        }
    }
}