I've been doing some testing with the TPL in C# to better understand how threads are created and when exactly new threads are kicked off.
With the below tests, The first one, using no async/await behaves as expected and always returns the values I would expect. However, the final 2 tests using async/await are very inconsistent.
In my tests, I made the following assumptions:
GetThreadIdInstantwould return on the same thread due to the task being completetd.GetThreadIdDelayedwould return on a different thread due to the delay not returning instantly.GetThreadIdForcedNewwould return on a different thread due to the use ofTask.Run().
Is there an explanation why the above assumptions are true when using .Result on a task, but not consistently true when using async/await?
Edit: Clarification on "inconsistent" tests: So the last 2 tests using async/await, I still expected them to give the same results as the first test using .Result, which is not true. However, the reason I have the code inside of a for loop is that some iterations work, and then later on my Assert statement will fail. The reason I used the word "inconsistent" is b/c continuously running the tests, and alternating between just running them versus debugging them causes them to sometimes pass and sometimes fail.
using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Threading; using System.Threading.Tasks; namespace Parallels.Tests { [TestClass] public class GetterTests { //this test always succeeds [TestMethod] public void ResultTest() { for (var i = 0; i < 500; i++) { var currentThreadId = Thread.CurrentThread.ManagedThreadId; var instantThreadId = ThreadGetter.GetThreadIdInstant().Result; var delayedThreadId = ThreadGetter.GetThreadIdDelayed().Result; var forcedNewThreadId = ThreadGetter.GetThreadIdForcedNew().Result; Assert.AreEqual(currentThreadId, instantThreadId); Assert.AreNotEqual(currentThreadId, delayedThreadId); Assert.AreNotEqual(currentThreadId, forcedNewThreadId); } } //mixed results [TestMethod] public async Task AwaitDelayedTest() { for (var i = 0; i < 500; i++) { try { var currentThreadId = Thread.CurrentThread.ManagedThreadId; var delayedThreadId = await ThreadGetter.GetThreadIdDelayed(); Assert.AreNotEqual(currentThreadId, delayedThreadId); } catch (Exception ex) { throw new Exception($"failed at iteration: {i}", ex); } } } //mixed results [TestMethod] public async Task AwaitForcedNewTest() { for (var i = 0; i < 500; i++) { try { var currentThreadId = Thread.CurrentThread.ManagedThreadId; var forcedNewThreadId = await ThreadGetter.GetThreadIdForcedNew(); Assert.AreNotEqual(currentThreadId, forcedNewThreadId); } catch (Exception ex) { throw new Exception($"failed at iteration: {i}", ex); } } } } public static class ThreadGetter { public static async Task<int> GetThreadIdInstant() => Thread.CurrentThread.ManagedThreadId; public static async Task<int> GetThreadIdDelayed() { await Task.Delay(1); return Thread.CurrentThread.ManagedThreadId; } public static async Task<int> GetThreadIdForcedNew() => await Task.Run(() => Thread.CurrentThread.ManagedThreadId); } }