简体   繁体   中英

How should I use DataflowBlockOptions.CancellationToken?

How could I use DataflowBlockOptions.CancellationToken ?

If I create instance of BufferBlock like this:

var queue = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 5, CancellationToken = _cts.Token });

then having consumer/producer methods that use queue , how can I use its CancellationToken to handle cancellation?

Eg in producer method, how can I check the cancellation token - I haven't found any property to access the token..

EDIT: Sample of produce/consume methods:

private static async Task Produce(BufferBlock<int> queue, IEnumerable<int> values)
{
    foreach (var value in values)
    {
        await queue.SendAsync(value);
    }

    queue.Complete();
}

private static async Task<IEnumerable<int>> Consume(BufferBlock<int> queue)
{
    var ret = new List<int>();
    while (await queue.OutputAvailableAsync())
    {
        ret.Add(await queue.ReceiveAsync());
    }

    return ret;
}

Code to call it:

var queue = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 5, CancellationToken = _cts.Token });

// Start the producer and consumer.
var values = Enumerable.Range(0, 10);
Produce(queue, values);
var consumer = Consume(queue);

// Wait for everything to complete.
await Task.WhenAll(consumer, queue.Completion);

EDIT2:

If I call _cts.Cancel() , the Produce method does not cancel and finishes without interruption.

If you want to cancel produce process you should pass token in it, like this:

    private static async Task Produce(
        BufferBlock<int> queue, 
        IEnumerable<int> values,
        CancellationToken token
        )
    {
        foreach (var value in values)
        {
            await queue.SendAsync(value, token);
            Console.WriteLine(value);
        }

        queue.Complete();
    }

    private static async Task<IEnumerable<int>> Consume(BufferBlock<int> queue)
    {
        var ret = new List<int>();
        while (await queue.OutputAvailableAsync())
        {
            ret.Add(await queue.ReceiveAsync());
        }

        return ret;
    }

    static void Main(string[] args)
    {
        var cts = new CancellationTokenSource();

        var queue = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 5, CancellationToken = cts.Token });

        // Start the producer and consumer.
        var values = Enumerable.Range(0, 100);
        Produce(queue, values, cts.Token);
        var consumer = Consume(queue);

        cts.Cancel();

        try
        {
            Task.WaitAll(consumer, queue.Completion);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

        foreach (var i in consumer.Result)
        {
            Console.WriteLine(i);
        }

        Console.ReadKey();

Normally you use the CancellationToken option in order to control the cancellation of a dataflow block, using an external CancellationTokenSource . Canceling the block has the following effects:

  1. The block stops accepting incoming messages (its Post method rejects the message by returning false ).
  2. The block stops offering the messages that are currently stored in its internal output buffer, to linked target blocks.
  3. The internal input and output buffers of the block are emptied.
  4. The Task that represents the Completion of the block transitions to the Canceled state. In other words this task's IsCanceled property becomes true

You can achieve all but the last effect directly, without using the CancellationToken option, by invoking the block's Fault method. This method is accessible through the IDataflowBlock interface that all blocks implement. You can use it like this:

((IDataflowBlock)bufferBlock).Fault(new OperationCanceledException());

The difference is that the Completion task will now become Faulted instead of Canceled . This difference may or may not be important, depending on the situation. If you just await the Completion , which is how this property is normally used, in both cases an OperationCanceledException will be thrown. So if you don't need to do anything fancy with the Completion property, and you also want to avoid configuring the CancellationToken for some reason, you could consider this trick as an option.

¹ In case the block is in the middle of processing one or more messages, these messages will be fully processed before the block is completed as canceled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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