Pergunta

From MSDN:

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.

My takeaway from the "async and await keywords don't cause additional threads to be created" is that it simply uses threads already created from the thread pool, which is fine, but the bit I have trouble understanding is "uses time on the thread when the method is active".

Does this mean that when I am awaiting the task, the task "moves" to the current thread or are the results marshalled back to the current thread? If you look at the output, the results come back on a different thread ID but complete on the original thread after they are all complete. I think I'm missing the "in between" of these two operations.

void Main()
{   
    var tasks = new List<Task<byte[]>>();

    Console.WriteLine ("Starting on Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    for (int i = 0; i < 5; i++)
    {
        tasks.Add(WriteFile(@"D:\Temp\" + i + ".txt", new String('*', i)));
    }

    Console.WriteLine ("Before Wait()ing on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    Task.WhenAll(tasks).Wait();

    Console.WriteLine ("Completed on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);
}

async Task<byte[]> WriteFile(string path, string text)
{
    FileStream fs = new FileStream(path, FileMode.Create);

    try
    {   
        Console.WriteLine ("Writing to file " + path + " . Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        var bytes = Encoding.Unicode.GetBytes(text);

        await fs.WriteAsync(bytes, 0, bytes.Length);

        Console.WriteLine ("Finished writing to file " + path  + ". Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        return new byte[0];
    }
    finally
    {
        Console.WriteLine ("Finally! " + path + ". Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        fs.Dispose();
    }
}

Output:

Starting on Managed Thread ID 46, Background True, Pool False

Writing to file D:\Temp\0.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\1.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\2.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\3.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True
Finally! D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True

Writing to file D:\Temp\4.txt . Managed Thread ID 46, Background True, Pool False
Before Wait()ing on Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True
Finally! D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True

Completed on Managed Thread ID 46, Background True, Pool False
Foi útil?

Solução

My takeaway from the "async and await keywords don't cause additional threads to be created" is that it simply uses threads already created from the thread pool

No, that's not true at all. async and await don't use the thread pool at all at a language level. It's entirely possible to write a program using async/await which only uses the initial "main" thread.

That doesn't mean that everything will happen on a single thread - it's just that async/await don't force extra threads to be created. The compiler itself doesn't generate any code to do that. It's entirely up to what you're awaiting. For example, you can task a task using Task.Run, which will (usually) use the thread pool, and you can await that. Or you can await the result of calling Task.Delay, which in itself doesn't start any threads but just schedules the task to be completed at a future time. Or you could use asynchronous IO, which may use IO completion ports but won't actually involve another thread running for most of the time that the task is "active".

A well-designed awaitable (if you're using anything built-in, this will include you) will schedule the continuation (all the code after await) in the same context as you were in when you started awaiting it, unless you've specifically told it not to. That "context" may mean that you come back to the same thread (e.g. in a UI) but may not (e.g. in the thread pool context, it doesn't matter which thread pool thread the continuation runs on).

In my view, it's worth learning more about what async really does under the hood. There are lots of resource around this, both free and behind paywalls:

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top