Right method to loop trough Task.Run in C# - c#

I would like to know if there 'right' method to loop one task forever?
What I've tried:
private static void A()
{
Task.Run(B);
}
private static async Task B()
{
while (true)
{
Task Delay = Task.Delay(10000);
await C();
await Delay;
}
}
private static async Task C()
{
// some async code that will work every 10s
}
It is working, but I want to understand is there any better options to do the same.
Thanks in advance.

Related

Wait for a specific result from Task then run another method in C#

I have a WPF app running on .net 6 and an external device connected to it.
Initializing the device sometimes fails and I don't want to hold the UI thread trying to initialize it.
I want to run the following method (_device.Init()) in an async fashion and when it returns true, run Start() method.
edit: run it until it returns true from the _device.Init() method, not true for finishing the task
Is there a built-in functionality to do it with tasks? or any other "best practice" way?
Thank you :)
SomeDevice _device = new();
public async void Init()
{
// some other code
while (Task.Run(() => _device.Init()).Result == false)
{
}
Start();
}
public void Start()
{
// some other code
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_device.Start();
}));
}
Instead of getting the Result of the Task (which may block the UI thread) you should await the Task:
public async void Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
The method should also be awaitable and be awaited when called, e.g. in an async Loaded event handler:
public async Task Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
...
await Init();
public async void Init()
{
var task = _device.Init();
//do work here
await task;
Start();
}
Should do the trick, it'll do the work and then wait for the task to complete before going to Start();
If you want to simply wait for init to finish and then run start it's even simpler with
await _device.Init().ContinueWith((x) => { Start();})

How to make async task run on background?

I have this code with TaskCompletionSource. I need to use in my console app. But the point is that it should be finished in background. If I now use await queries.MarkRandomTaskWithDelay(1000) it waits 1000 ms and then I can continue working with app. But I need to continue working immediately and only then to receive result. How can I do it?
public static class queries
{
public static TaskCompletionSource<int> completionSource = new TaskCompletionSource<int>();
public static Task<int> MarkRandomTaskWithDelay(int interval)
{
Timer timer = new Timer(interval)
{
AutoReset = false
};
timer.Elapsed += Marking;
timer.Start();
return completionSource.Task;
}
private async static void Marking(object o, ElapsedEventArgs e)
{
try
{
await SomeWork();
Console.WriteLine($"\nTask status with ID {task.Id} was changed to 'finished' in background.\n");
completionSource.SetResult(task.Id);
}
catch (Exception ex)
{
completionSource.SetException(ex);
}
}
}
Don't use await until you actually need the result.
//Start the task but don't wait for it to finish
Task<int> task = queries.MarkRandomTaskWithDelay(1000);
//Do whatever work we can do without knowing the result
DoOtherWork();
//Now we need the result, so we await the original task
int result = await task;
UseTheResult(result);

execute after x seconds - use async task or timer ticks

currently I'm using a timer to poll every x seconds. I've seen that I could also use asyncronous tasks to execute a function after x seconds.
So I've created an example for reproduction. This is how I would use a polling timer
class UseTimer
{
public UseTimer()
{
Console.WriteLine("Foo");
Timer myTimer = new Timer(2000);
myTimer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
Console.WriteLine("Bar");
myTimer.Enabled = false;
};
myTimer.Enabled = true;
Console.ReadLine();
}
}
The code first logs Foo, then waits 2 seconds for the first timer tick and then logs Bar. I tried to reproduce it by using async/await
class UseAsync
{
public UseAsync()
{
Console.WriteLine("Foo");
Do().Wait();
Console.ReadLine();
}
private async Task Do()
{
await Task.Delay(2000);
Console.WriteLine("Bar");
}
}
The behaviour seems to be the same when I test it with this code
class Program
{
static void Main(string[] args)
{
// UseAsync a = new UseAsync();
UseTimer t = new UseTimer();
}
}
I would like to know if I could or even should switch to async because it's easier to maintain and takes out complexity but remains the same way under the hood.
"Every x seconds" is different from "after x seconds".
If you need to run something (repeatedly) every x seconds, use a Timer.
If you need to run something (only once) after x seconds, use Task.Delay.
As noted in the comments, Task.Delay uses a System.Threading.Timer anyway, it's just easier to use for a single wait, and keeps your code clean.
Also, it's not wise to use asynchronous methods in a class constructor. Class constructors cannot be async, and thus you end up blocking the thread (as you did when using Wait()), or "fire and forget". If you need to do anything asynchronous while creating a new object, you can use a "factory method": a static method that creates the object for you. Then you make the constructor private to force everyone to use the factory method:
class UseAsync
{
private UseAsync() {}
public static async Task<UseAsync> CreateUseAsync()
{
var myC = new UseAsync();
await myC.Do();
return myC;
}
private async Task Do()
{
await Task.Delay(2000);
Console.WriteLine("Bar");
}
}
Then you can create an instance like this:
var a = await UseAsync.CreateUseAsync();
I've done this when I need to retrieve data from somewhere before an object is actually useful.
The console.readline should be outside useAsync method, if not the task Do will not be executed
class Program
{
static void Main(string[] args)
{
UseAsync.UseAsyn();
Console.ReadLine();
}
}
static class UseAsync
{
public static async Task UseAsyn()
{
Console.WriteLine("Foo");
await Do();
}
private static async Task Do()
{
await Task.Delay(2000);
Console.WriteLine("Bar");
}
}

How to use async to execute callback delegate

I have a callback function which needs to takes several seconds to process and should be a async method, but I can't find a way to execute this async callback by await because it must be a Delegate param in the calling method.
Here is some piece of code:
async Task Callback(){//do some callback..}
async Task DoSomething(Func<Task> callback){//I want to execute the callback like: await callback();}
async void Main(){ DoSomething(Callback);}
Sorry for my poor english, any idea to do that? Thanks!
You will have to await first call itself.
change
async void Main(){ DoSomething(Callback);}
to
async void Main(){ await DoSomething(Callback);}
After that It should work, I tested with your sample code. Please verify at your end.
class Program
{
static void Main(string[] args)
{
(new Test()).Main();
Console.ReadKey();
}
}
public class Test
{
async Task Callback()
{
Console.WriteLine("I'm in callback");
}
async Task DoSomething(Func<Task> callback)
{
Console.WriteLine("I'm in DoSomething");
await callback();
}
public async void Main()
{
Console.WriteLine("I'm in Main");
await DoSomething(Callback);
Console.WriteLine("Execution completed");
}
}
Here is output

Asynchronous flow of C# program

To demonstrate asynchronous flow of C# I have written a simple program
( To show its difference from Python, as Python is synchronous because of GIL).
Why is execution of func2() waiting for func1() to finish?
void main()
{
Console.WriteLine("main");
func1();
func2();
}
public void func1()
{
Console.WriteLine("func1");
double i = 0;
while(true)
{
i += 1;
if (i > 100000000)
{
break;
}
}
func(3);
func(4);
}
public void func2()
{
Console.WriteLine("func2");
func(5);
func(6);
}
public void func(int number)
{
Console.WriteLine(number);
}
In func1(), I am running a loop to make program wait so that main() keep going and call func2() before func(3) and func(4). But every time, it runs in a synchronous fashion and print output in this order :
main
func1
3
4
func2
5
6
i.e. func2() is always called after func4() which I didn't expect in asynchronous flow.
Why is execution of func2() waiting for func1() to finish?
Thanks
C# does not make your program synchronous. All programs are synchronous (exception of multi-core). Even the OS runs synchronously and only gives the illusion of parallel processing by giving various programs time slices of execution. If you want your program to run parallel you have to explicitly tell it so. C#/.NET have many mechanisms to do this but it is not fair to say a language is asynchronous. Multi-thread code can be written in C to run on windows but if you are working on an embedded system that doesn't support parallel processing you cant.
If you want func2 not to wait for func1 to finish, you need to tell it so:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("main");
doWork();
}
public async static void doWork()
{
Task<int> longTask = func1(); //Start func1
func2(); //Call func2 while we wait
int output = await longTask; //Wait for func2 to be finished
}
public async static Task<int> func1()
{
Console.WriteLine("func1");
await Task.Delay(10000); //delay, could make func(3) and func(4) run in the meantime if we wanted....
func(3);
func(4);
return 0;
}
public static void func2()
{
Console.WriteLine("func2");
func(5);
func(6);
}
public static void func(int number)
{
Console.WriteLine(number);
}
}
Outputs:
main
func1
func2
5
6
3
4
This happens because every time a method is invoked, in C#, the information passed to the parameter is stored in a stack (call stack). This stack is implemented as first in last out.
So in your example when the func1 is invoked it goes to stack, the main method will stop and wait for the func1 to leave the stack.
https://en.wikipedia.org/wiki/Call_stack
There is no way to do what you want without asynchronous flow and I suspect it is the same for python, too.
However you can easily change your code to execute it asynchronous.
public static void Main()
{
Console.WriteLine("main");
var t = func1();
func2();
t.Wait();
}
public static Task func1()
{
Console.WriteLine("func1");
return Task.Factory.StartNew(() => {
double i = 0;
while(true)
{
i += 1;
if (i > 100000000)
{
break;
}
}
func(3);
func(4);
});
}
public static void func2()
{
Console.WriteLine("func2");
func(5);
func(6);
}
public static void func(int number)
{
Console.WriteLine(number);
}

Categories