识别TPL数据流中的同时任务

时间:2017-06-26 01:16:50

标签: c# task-parallel-library tpl-dataflow

我在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池中回收?

1 个答案:

答案 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);
        }
    }
}