我有一个“昂贵”的第三方组件。该组件不是线程安全的。所述组件托管在WCF服务中(暂时),因此......每次调用进入服务时,我都必须新建组件。
我想要做的是拥有一个说16个线程的池,每个线程都会启动它们自己的组件副本,并且有一个机制来调用该方法并将其分配给16个线程中的一个并拥有返回值。
如此简单:
var response = threadPool.CallMethod(param1, param2);
对于阻止调用它很好,直到它得到响应,因为我需要响应才能继续。
有什么建议吗?也许我正在过度思考它并且由16个线程提供服务的ConcurrentQueue
可以完成这项工作,但是现在确定方法返回值将如何返回给调用者?
答案 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);
}
}
}