延迟发布到DataFlow

时间:2017-03-16 13:26:30

标签: c# tpl-dataflow dataflow

将项目发布到TPL DataFlow时,是否有任何机制可以允许延迟发布?

public partial class BasicDataFlowService
{
    private readonly ActionBlock<string> workerBlock;

    public BasicDataFlowService()
    {
        workerBlock = new ActionBlock<string>(file => DoWork(file), new ExecutionDataflowBlockOptions()
        {
            MaxDegreeOfParallelism = 32
        });
    }

    partial void DoWork(string fileName);

    private void AddToDataFlow(string file)
    {
        workerBlock.Post(file);
    }
}

AddToDataFlow内,我希望能够在处理项目之前指定延迟(例如,如果我们决定将处理推迟30秒)。

我确实考虑过将TransFormBlocknew System.Threading.ManualResetEvent(false).WaitOne(1000);一起使用,例如

var requeueBlock = new TransformBlock<string, string>(file =>
{
    new System.Threading.ManualResetEvent(false).WaitOne(1000);
    return file;
});

requeueBlock.LinkTo(workerBlock);

然而,这似乎消耗了一个不必要的线程,可以被链中的其他块使用。

2 个答案:

答案 0 :(得分:0)

首先,您需要将ManualResetEvent存储为单身,否则所有线程都会有自己的对象等待,而您的方法将无法正常工作。

其次,如果您需要在管道中的AppDomain内进行同步,请考虑ManualResetEventSlim版本而不是重ManualResetEvent

如果您希望重用机器的内核而无需等待,您应该研究SpinWait轻量级结构。在这种情况下,您可能会发现Joseph Albahari' article非常有用:

// singleton variable
bool _proceed;

var requeueBlock = new TransformBlock<string, string>(file =>
{
    var spinWait = new SpinWait();
    while (!_proceed)
    {
        // ensure we have the latest _proceed value
        Thread.MemoryBarrier();
        // try to spin for a while
        // after some spins, yield to another thread
        spinWait.SpinOnce();
    }
    return file;
});

SpinWait在内部决定如何:使用Sleep(0)Sleep(1)Yield方法调用,这对您的案例非常有效。

答案 1 :(得分:0)

要在将值发布到workerBlock之前添加延迟,您只需插入延迟并在发布值之前等待它。如果workerBlock具有有限容量,您可以await SendAsync。完成目标的几个选项:

private async Task AddToDataflow(string file, TimeSpan delay) {
    await Task.Delay(delay);
    await workerBlock.SendAsync(file);
}

private async Task AddToDataflow(string file) {
    var delay = TimeSpan.FromSeconds(30);
    await Task.Delay(delay);
    await workerBlock.SendAsync(file);
}

private async void AddToDataflow(string file) {
    var delay = TimeSpan.FromSeconds(30);
    await Task.Delay(delay);
    workerBlock.Post(file);
}