0

I'm writing a console application that needs to copy console input one keypress at a time to a socket (while also doing other things). In order to do that, I figure I have a couple options. In the before times, I would've spun up a thread to do the work, but for this particular implementation, I've decided to use a Task instead. This has led to some confusion on my part.

I have this simple function:

private static async Task ReadConsoleInput(Socket socket, Encoding encoding, CancellationToken cancellationToken)
{
    var inputCharacter = new char[1]; // we only input one character at a time
    var convertedBytes = new byte[4]; // four should be enough for anyone!

    while (!cancellationToken.IsCancellationRequested) {
        var readKey = Console.ReadKey(true);

        inputCharacter[0] = readKey.KeyChar;

        var byteCount = encoding.GetBytes(inputCharacter, convertedBytes);

        await socket.SendAsync(convertedBytes.AsMemory(0, byteCount), cancellationToken);
    }
}

If I start a Task for this in this way:

 var consoleReadTask = Task.Factory.StartNew(
    async () => await ReadConsoleInput(socket, encoding, tokenSource.Token),
    tokenSource.Token,
    TaskCreationOptions.None,
    TaskScheduler.Default);

Everything functions correctly. This seems quite odd to me, however, because ReadConsoleInput, being async, already returns a Task. Thus, I figured it would be exactly the same to start the Task in this way:

var consoleReadTask = ReadConsoleInput(socket, encoding, tokenSource.Token);

This turned out to be incorrect, however. My program hangs (somewhere else, I haven't looked into it, because that's not the point here) before I ever read any input.

To me, it seems like these two methods are functionally similar, if not identical. The Task.Factory.StartNew implementation seems weird, because normally one wouldn't start a Task with an async lambda (right? am I crazy?), but both should function, I'd think.

Why are these two implementations different?

10
  • maybe your program depends on the first console.readkey not being on the same thread that executes the non-taskfactory method? Commented Mar 6 at 19:04
  • 1
    You mean because I call the blocking Console.ReadKey before the first await? That would mean the async/await state machine doesn't return a Task until the first await is reached? I'm not sure that's correct, because some stuff happens before it hangs... Commented Mar 6 at 19:10
  • that would be my intuition - yes. Commented Mar 6 at 19:16
  • Is there something about Console I/O in Windows that would cause this issue? Commented Mar 6 at 19:34
  • "That would mean the async/await state machine doesn't return a Task until the first await is reached" - that's exactly how async methods work, they are executed synchronously until first await of "truly synchronous" operation. Try adding await Task.Yield() or await Task.Delay(1) before your while loop. Commented Mar 6 at 21:28

1 Answer 1

0

I'm unable to comment but would like to share an opinion:

Task.Factory.StartNew is queueing the callback on to the ThreadPool. This can be seen if one looks at the source code:

Which means that the Console.ReadKey is running in the background thread. This is interesting because the Task will not be started immediately, which gives time for other stuff to happen in the mean time (unlike the direct approach).

The main difference between these two is that the former one will continue executing while the latter will block until a key is read (in both examples the Task is not awaited).

Calling the ReadConsoleInput directly is what is causing the issue, here's why I think that: somewhere before ReadConsoleInput is called you are calling some other un-awaited async Console method which does some kind of reading (Console.In.[...], Console.Out.[...] etc.) or something else Console related.

Both Console.ReadKey and the other method are in a deadlock because both are locking on themselves:

Reason why starting the process with TaskFactory is working is because the other method completed its work or got disposed.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.