Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Console.ReadKey() not working

Tags:

c#

.net

Please look at original Microsoft example.

In the second example we find the following code:

// The simplest UI thread ever invented.
Task.Run(() =>
{
    if (Console.ReadKey().KeyChar == 'c')
        cts.Cancel();
});

(Full code needed to run this:)

static int inputs = 2000;
static void Main(string[] args)
{
    // The token source for issuing the cancelation request.
    CancellationTokenSource cts = new CancellationTokenSource();

    // A blocking collection that can hold no more than 100 items at a time.
    BlockingCollection<int> numberCollection = new BlockingCollection<int>(100);

    // Set console buffer to hold our prodigious output.
    //Console.SetBufferSize(80, 2000);

    // The simplest UI thread ever invented.
    Task.Run(() =>
    {
        if (Console.ReadKey().KeyChar == 'c')
            cts.Cancel();
        else
        {
            Debugger.Break();
        }
    });

    // Start one producer and one consumer.
    Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token));
    Task.Run(() => NonBlockingProducer(numberCollection, cts.Token));

    Console.WriteLine("Press the Enter key to exit.");
    Console.ReadLine();
}

static void NonBlockingConsumer(BlockingCollection<int> bc, CancellationToken ct)
{
    // IsCompleted == (IsAddingCompleted && Count == 0)
    while (!bc.IsCompleted)
    {
        int nextItem = 0;
        try
        {
            if (!bc.TryTake(out nextItem, 0, ct))
            {
                Console.WriteLine(" Take Blocked");
            }
            else
                Console.WriteLine(" Take:{0}", nextItem);
        }

        catch (OperationCanceledException)
        {
            Console.WriteLine("Taking canceled.");
            break;
        }

        // Slow down consumer just a little to cause
        // collection to fill up faster, and lead to "AddBlocked"
        Thread.SpinWait(500000);
    }

    Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit.");
}

static void NonBlockingProducer(BlockingCollection<int> bc, CancellationToken ct)
{
    int itemToAdd = 0;
    bool success = false;

    do
    {
        // Cancellation causes OCE. We know how to handle it.
        try
        {
            // A shorter timeout causes more failures.
            success = bc.TryAdd(itemToAdd, 2, ct);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Add loop canceled.");
            // Let other threads know we're done in case
            // they aren't monitoring the cancellation token.
            bc.CompleteAdding();
            break;
        }

        if (success)
        {
            Console.WriteLine(" Add:{0}", itemToAdd);
            itemToAdd++;
        }
        else
        {
            Console.Write(" AddBlocked:{0} Count = {1}", itemToAdd.ToString(), bc.Count);
            // Don't increment nextItem. Try again on next iteration.

            //Do something else useful instead.
            UpdateProgress(itemToAdd);
        }

    } while (itemToAdd < inputs);

    // No lock required here because only one producer.
    bc.CompleteAdding();
}

static void UpdateProgress(int i)
{
    double percent = ((double)i / inputs) * 100;
    Console.WriteLine("Percent complete: {0}", percent);
}

What that code should do is totally clear: it should break on pressing c, but it doesn't work. Instead it runs until the end asking to close with Enter.

How can we fix this?

It seems to be a threading problem, but it's a demo for .net4.5 and the code is not working.

The KeyPress of 'c' does not set the CancelationToken.

like image 706
GreenEyedAndy Avatar asked Sep 27 '22 07:09

GreenEyedAndy


1 Answers

You are totally right on the example being wrong. It is just broken.

The reason it doesn't work is that it doesn't wait on the completion of the Tasks, instead it waits on the Console.ReadLine. This will fix it:

Replace:

Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token));
Task.Run(() => NonBlockingProducer(numberCollection, cts.Token));

With:

Task t1 = Task.Run(() => NonBlockingConsumer(numberCollection, cts.Token));
Task t2 = Task.Run(() => NonBlockingProducer(numberCollection, cts.Token));

Task.WaitAll(t1, t2);
like image 191
Patrick Hofman Avatar answered Oct 11 '22 15:10

Patrick Hofman