繁体   English   中英

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

[英]“Fixed” / “Load Balanced” C# thread pool?

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

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

所以很简单:

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

它可以阻止调用,直到它得到响应,因为我需要响应才能继续。

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

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

我会做你的情况是只使用一个单一 的ThreadLocal或线程静态,将与你昂贵的对象,一旦得到初始化。 此后它将可用于线程池线程。

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

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

首先,我同意@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);
    }
}

如果您在原始类或结构(即ObjectPool<MyComponent> )方面考虑类型T ,这似乎并不是非常有用,因为池没有内置任何线程控件。但是您可以用类型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或线程静态字段中维护组件更复杂,但是如果你需要做一些像限制池化实例数量这样的事情,那么池抽象可能非常有用。

编辑

基本的“固定的X实例集”池实现,其中一旦池被耗尽, 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);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM