Question

I have a Windows form with three buttons. One button adds entries to a BlockingCollection. One starts processing the list and one stops processing the list.

I can add entries to my BlockingCollection and when I click Start, the list is consumed as I would expect. I can still add new items and they continue to be consumed. However when I click the Stop button, although the tasks DO stop, I cannot start them again with the start button.

What am I doing wrong in the cancellation of the tasks, that they won't start again? I've read countless items on cancelling tasks but still don't 'get it'.

Any help would be great. Here's the code...

    // Blocking list for thread safe queuing
    private BlockingCollection<QueueItem> ItemList = new BlockingCollection<QueueItem>();
    private CancellationTokenSource CancelTokenSource = new CancellationTokenSource();
    private int MaxConsumers = 3;

    // Form initialisation
    public MainForm()
    {
        InitializeComponent();
    }

    // Create an async consumer and add to managed list
    private void CreateConsumer(int iIdentifier)
    {
        Task consumer = Task.Factory.StartNew(() =>
        {
            foreach (QueueItem item in ItemList.GetConsumingEnumerable())
            {
                Console.WriteLine("Consumer " + iIdentifier.ToString() + ": PROCESSED " + item.DataName);
                Thread.Sleep(894);

                if (CancelTokenSource.IsCancellationRequested)
                    break; // Cancel request
            }
        }, CancelTokenSource.Token);
    }

    // Add a new item to the queue
    private void buttonAdd_Click(object sender, EventArgs e)
    {
        for (int i = 0; i < 10; i++)
        {
            Task.Factory.StartNew(() =>
            {
                // Add an item
                QueueItem newItem = new QueueItem(RandomString(12));

                // Add to blocking queue
                ItemList.Add(newItem);
            });
        }
    }

    // Start button clicked
    private void buttonStart_Click(object sender, EventArgs e)
    {
        buttonStart.Enabled = false;
        buttonStop.Enabled = true;

        // Create consumers
        for (int i = 0; i < MaxConsumers; i++)
        {
            CreateConsumer(i);
        }
    }

    // Stop button clicked
    private void buttonStop_Click(object sender, EventArgs e)
    {
        CancelTokenSource.Cancel();

        buttonStop.Enabled = false;
        buttonStart.Enabled = true;
    }

    // Generate random number
    private static Random random = new Random((int)DateTime.Now.Ticks);
    private string RandomString(int size)
    {
        StringBuilder builder = new StringBuilder();
        char ch;
        for (int i = 0; i < size; i++)
        {
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
            builder.Append(ch);
        }

        return builder.ToString();
    }
Was it helpful?

Solution

You re-use the same cancellation token that is still configured to cancel.

So create a new token when starting new tasks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top