0

I'm creating a thread

Thread MyThread = new Thread(async () => await MyTask());
MyThread.Start(); 

It kinda doesn't make sense and that's the question of how to create it properly.

MyTask is an unending task which periodically starts cpu intensive work

async Task MyTask ()
    {
        while (true)
        {                
            await Task.Run(() => CPU_Load());
            await Task.Delay(1000).ConfigureAwait(false);
        }
    }

CPU_Load is a method where work is being done

void CPU_Load ()
    {            
       *CPU_Load*    
    }


      
Ken White
  • 123,280
  • 14
  • 225
  • 444
IllSkillz
  • 3
  • 3
  • Why not use the thread safe [System.Threading.Timer](https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-6.0) instead? – Igor Feb 01 '22 at 20:44
  • 3
    What is your question exactly? – Charlieface Feb 01 '22 at 20:54
  • The question is hot to create a thread? As I've been told the thread class is not aware of async/await, so it executes the method up until it meets the first await and then stops. – IllSkillz Feb 01 '22 at 21:00
  • I have never worked with timers, I took a quick look and i dont think its suitable for me. – IllSkillz Feb 01 '22 at 21:19
  • @IllSkillz Why isn't a Timer suitable? *"periodically starts cpu intensive work"* - that's what timers do. If you create a thread that runs code, waits, then runs that code again, you've just written your own timer. – Gabriel Luci Feb 01 '22 at 23:18
  • In microsoft example method that is called by the timer delegate is in a different class, and that's the problem because there's global variables that is used by the thread and main code. Also there's an IRC client and Thread is created for each message, I dont think that timer is suitable for that. Is it wrong to use Threads with await inside them? I mean 2 people are suggesting something else... – IllSkillz Feb 02 '22 at 00:15
  • How about the original async strategy back when `Main` couldn't be `async`. Start your thread with a non-async function (or delegate or lambda). In that function that simply says `WhatIWantToDoAsync().Wait();` that is async and returns a Task. Your thread function never returns, so waiting for it isn't going to do much. – Flydog57 Feb 02 '22 at 00:38
  • *"In microsoft example method that is called by the timer delegate is in a different class"* - That's just the example. It's not a requirement. The timer delegate can be in any class. – Gabriel Luci Feb 02 '22 at 00:48
  • *"Is it wrong to use Threads with await inside them?"* - No, but you're just reinventing a timer. – Gabriel Luci Feb 02 '22 at 00:48
  • That seems to be working! Thanks! – IllSkillz Feb 02 '22 at 01:17
  • Ill take a deeper look at timers, thanks – IllSkillz Feb 02 '22 at 01:19
  • also a thread works fine with async await. – pm100 Feb 02 '22 at 01:28
  • Recommended reading; https://blog.stephencleary.com/2013/11/there-is-no-thread.html – Jeremy Lakeman Feb 02 '22 at 03:11

2 Answers2

2

The Thread constructor does not understand async delegates. You can read about this here:

The "proper" way to execute periodically some code depends on where you want to run your code. Do you have any reason to run it on a dedicated thread? Some components are thread-affine, and require to be manipulated by the same thread for the entirety of their existence. If you have this (not very common) requirement, you can use the Thread constructor without async/await:

var myThread = new Thread(() =>
{
    while (true)
    {
        var delayTask = Task.Delay(1000);
        CPU_Load();
        delayTask.Wait();
    }
});
myThread.IsBackground = true;
myThread.Start();

Notice how the Task.Delay task is created before the CPU-bound operation, and then waited afterwards. This way the interval between two subsequent invocations of the CPU_Load method will be constant. It will not depend on the duration of the call itself.

If you don't need a dedicated thread, you can do the work more economically by using reusable threads from the ThreadPool. Which is exactly what your current MyTask implementation does, with the Task.Run method inside. To start the task is as easy as invoking the asynchronous method:

var myTask = MyTask();

Now the task is running, and will continue running until the process terminates, or the CPU_Load invocation fails, whatever comes first.

Another way to implement the asynchronous MyTask method would be to wrap the whole loop in a Task.Run, instead of having the Task.Run inside the loop. Functionally and performance-wise is almost identical:

var myTask = Task.Run(async () =>
{
    while (true)
    {
        var delayTask = Task.Delay(1000);
        CPU_Load();
        await delayTask;
    }
});

I have omitted the ConfigureAwait(false) because it's not really needed in this case. The ThreadPool does not have a synchronization context that can be captured by the await.

You could also consider using a Timer to run periodically your work, but in my opinion the Task-based approach is superior. It's quite tricky to enforce a non-overlapping execution policy with constant interval using a Timer. Caveats:

  1. The System.Timers.Timer class is not thread-safe.
  2. It allows overlapping invocations of the event handler.
  3. It swallows any exceptions thrown inside the handler.
  4. It is possible for the Elapsed event to be raised after stopping the Timer with the Stop method.
  5. There is no easy way to stop the timer, and then wait for all running event handlers to complete.
  6. The class is disposable and needs to be disposed.
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
0

Quoting a comment by Flydog57:

How about the original async strategy back when Main couldn't be async. Start your thread with a non-async function (or delegate or lambda). In that function that simply says WhatIWantToDoAsync().Wait(); that is async and returns a Task. Your thread function never returns, so waiting for it isn't going to do much.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
IllSkillz
  • 3
  • 3
  • 3
    If flydog57's suggestion was helpful then please ask him/her to leave a post so he/she could receive reputation for that. – Peter Csala Feb 02 '22 at 03:52
  • This may work as a workaround but Task.Wait blocks the invoking thread, in this case for the whole run of the application. So the newly created thread will only run until first suspend and then block until the while loop ends. You'd be better off doing everything synchronously and using Thread.Sleep. Or use Timer as suggested. – Zdeněk Jelínek Feb 02 '22 at 06:35