我在TPL数据流块中有1000个元素, 每个元素都将调用外部Web服务。
网络服务最多支持10个同时通话, 这可以通过以下方式轻松实现:
new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 10
...
}
Web服务要求每个调用都有一个唯一的id,它将其与其他同时调用区分开来。 理论上这应该是一个guid,但实际上第11个GUID将失败 - 因为服务器上的限制机制很难识别第一个调用已完成。
供应商建议我们回收guids,保持10个正在使用。
我打算有一个GUID数组,每个任务都会使用(Interlocked.Increment(ref COUNTER)%10)作为数组索引
编辑: 我才意识到这不会奏效! 它假定任务将按顺序完成,而不是 我可以将其作为一个ID队列实现,其中每个任务都借用并返回一个,但问题仍然存在,是否有一种更简单,更具优势的线程安全方法来执行此操作?
(对于COUNTER来说,永远不会有足够的呼叫溢出)
但我已经多次对C#(我是.net的新手)感到惊讶,我正在实施已经存在的东西。
是否有更好的线程安全方式让每个任务从ID池中回收?
答案 0 :(得分:3)
创建资源池的确切情况System.Collections.ConcurrentBag<T>
非常有用。将其包裹在BlockingCollection<T>
中以使代码更容易。
class Example
{
private readonly BlockingCollection<Guid> _guidPool;
private readonly TransformBlock<Foo, Bar> _transform;
public Example(int concurrentLimit)
{
_guidPool = new BlockingCollection<Guid>(new ConcurrentBag<Guid>(), concurrentLimit)
for(int i = 0: i < concurrentLimit; i++)
{
_guidPool.Add(Guid.NewGuid());
}
_transform = new TransformBlock<Foo, Bar>(() => SomeAction,
new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = concurrentLimit
//...
});
//...
}
private async Task<Bar> SomeAction(Foo foo)
{
var id= _guidPool.Take();
try
{
//...
}
finally
{
_guidPool.Add(id);
}
}
}