We help IT Professionals succeed at work.

Can a Task.Run().Wait() task end up being "inlined"?

deleyd
deleyd asked
on
Is it possible for the following task to be "inlined" and run on the current thread when the t.Wait(); is encountered?
Task t = Task.Run(() => MyMethod());
t.Wait();

Open in new window

If so, can you provide some example code that does this?

(I've tried setting ThreadPool.SetMaxThreads(8, 8); (This succeeds. My computer has 8 CPUs.) and then launching a dozen tasks via Task.Run() to try and fill up the ThreadPool so my Task t = Task.Run() gets queued but not run. However, I have not been able to get t.Wait(); to "inline to inline the task. So far it just always waits I assume for a free ThreadPool thread to become available.)
Comment
Watch Question

Eduard GherguArchitect - Coder - Mentor

Commented:
Hi,

Why are you calling ThreadPool.SetMaxThreads(8, 8)?
it_saigeDeveloper
Distinguished Expert 2019

Commented:
Short answer is no.

You cannot inline the wait call in that fashion since the Wait method returns a bool and the Run method returns a Task.

If you need to wait for all of your tasks to finish before returning the results, then you should add your tasks to a list and use Task.WaitAll; e.g. -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EE_Q29170256
{
    class Program
    {
        static void Main(string[] args)
        {
            var tasks = new List<Task<int>>(from i in Enumerable.Range(0, 5) select Task.Run(() => 
            {
                Task.Delay(1000);
                return i;
            }));

            Task.WaitAll(tasks.ToArray());
            foreach(var task in tasks)
            {
                Console.WriteLine($"Task {task.Id} resulted in {task.Result}");
            }
            Console.ReadLine();
        }
    }
}

Open in new window

Which produces the following output -Capture.PNG-saige-
deleydSoftware Engineer

Author

Commented:
My goal is to get the task to be "inlined" by the t.Wait(); statement.
Setting Max Threads to a low value was part of my attempt to accomplish this goal.

The Microsoft documentation for Task.Wait() states:

Wait is a synchronization method that causes the calling thread to wait until the current task has completed. If the current task has not started execution, the Wait method attempts to remove the task from the scheduler and execute it inline on the current thread. If it is unable to do that, or if the current task has already started execution, it blocks the calling thread until the task completes. For more information, see Task.Wait and "Inlining" in the Parallel Programming with .NET blog. https://devblogs.microsoft.com/pfxteam/task-wait-and-inlining/

I can accomplish this by doing:
Task t = MyMethod();
t.Wait();

Open in new window

I'm trying to determine if inlining can also happen if I first call Task.Run().
deleydSoftware Engineer

Author

Commented:
Here is my current attempt to get a task to inline:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadConsoleApp6
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowMaxThreads();
            ShowAvailableThreads();
            bool stat = ThreadPool.SetMaxThreads(8, 8);
            ShowAvailableThreads();

            var tasks = new List<Task>();
            for (int i = 0; i < 8; ++i)
            {
                tasks.Add(Task.Run(() => Wait5Seconds()));
            }
            Thread.Sleep(1000);

            ShowAvailableThreads();
            Thread thread = Thread.CurrentThread;
            int threadId = thread.ManagedThreadId;
            Console.WriteLine("Main Thread:  {0:N0}", threadId);
            Console.WriteLine("Main thread is available. Let's run AnotherTask()");

            Task t = Task.Run(() => AnotherTask());
            t.Wait();  // We're waiting. Why does it not inline task and run it on main thread?

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Press any key");
            Console.ReadKey();
        }

        static private void AnotherTask()
        {
            Thread thread = Thread.CurrentThread;
            int threadId = thread.ManagedThreadId;
            Console.WriteLine("Another Task Thread:  {0:N0}", threadId);
        }

        static void Wait5Seconds()
        {
            Thread.Sleep(5000);
        }

        static private void ShowMaxThreads()
        {
            int workerThreads;
            int portThreads;
            ThreadPool.GetMaxThreads(out workerThreads, out portThreads);
            Console.WriteLine("\nMaximum worker threads: \t{0}\nMaximum completion port threads: {1}", workerThreads, portThreads);
        }

        static private void ShowAvailableThreads()
        {
            int workerThreads;
            int portThreads;
            ThreadPool.GetAvailableThreads(out workerThreads, out portThreads);
            Console.WriteLine("\nAvailable worker threads: \t{0}\nAvailable completion port threads: {1}\n", workerThreads, portThreads);
        }
    }
}

Open in new window

I set max threads to 8 because that's the minimum I can for my machine. I then spin off 8 tasks so there are no more available ThreadPool threads. I then try to get AnotherTask() to be inlined, but it insists on waiting instead.
Eduard GherguArchitect - Coder - Mentor

Commented:
Hi,
There is no warranty to have the inline performed if Task.Run() is invoked as long as it just enqueue the execution of the specified delegate. In the end, what you're trying to obtain?
Software Engineer
Commented:
I got it to work. Had to spin off my Test() so my Test was running on a ThreadPool thread.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadConsoleApp6
{
    class Program
    {
        static void Main(string[] args)
        {
            ShowMaxThreads();
            ShowAvailableThreads();
            bool stat = ThreadPool.SetMaxThreads(8, 8);
            ShowAvailableThreads();

            Task t1 = Task.Run(() => Test());
            t1.Wait();

            Console.WriteLine("Press any key");
            Console.ReadKey();
        }

        static void Test()
        {
            var tasks = new List<Task>();
            for (int i = 0; i < 16; ++i)
            {
                tasks.Add(Task.Run(() => Wait5Seconds()));
            }
            Thread.Sleep(1000);

            ShowAvailableThreads();
            Thread thread = Thread.CurrentThread;
            int threadId = thread.ManagedThreadId;
            Console.WriteLine("Test Thread:  {0:N0}", threadId);
            Console.WriteLine("Test thread is available. Let's run AnotherTask()");

            Task t = Task.Run(() => AnotherTask());
            t.Wait();  // We're waiting. Why does it not inline task and run it on main thread?

            Task.WaitAll(tasks.ToArray());
        }

        static private void AnotherTask()
        {
            Thread thread = Thread.CurrentThread;
            int threadId = thread.ManagedThreadId;
            Console.WriteLine("Another Task Thread:  {0:N0}", threadId);
        }

        static void Wait5Seconds()
        {
            Thread.Sleep(5000);
        }

        static private void ShowMaxThreads()
        {
            int workerThreads;
            int portThreads;
            ThreadPool.GetMaxThreads(out workerThreads, out portThreads);
            Console.WriteLine("\nMaximum worker threads: \t{0}\nMaximum completion port threads: {1}", workerThreads, portThreads);
        }

        static private void ShowAvailableThreads()
        {
            int workerThreads;
            int portThreads;
            ThreadPool.GetAvailableThreads(out workerThreads, out portThreads);
            Console.WriteLine("\nAvailable worker threads: \t{0}\nAvailable completion port threads: {1}\n", workerThreads, portThreads);
        }
    }
}

Open in new window