I create 2 methods that print x and y 100 times. I want them to run concurrent and I expect the output to be xxxxyxyxyyyxyyxyx... sthg like that.
It doesn't print anything. Am I missing some logic here?
using System;
using System.Threading.Tasks;
namespace ConsoleApplication32
{
internal class Program
{
public static async Task<int> Print1()
{
await Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
Console.Write("x");
}
});
return 1;
}
public static async Task<int> Print2()
{
await Task.Run(() =>
{
for (int i = 0; i < 100; i++)
{
Console.Write("y");
}
});
return 1;
}
public static void Run()
{
Task<int> i = Print1();
Task<int> k = Print2();
}
private static void Main(string[] args)
{
Run();
}
}
}
Explanation
There are a couple things we need to tweak to get the desired output: xxxxyxyxyyyxyyxyx.
The Run method is not waiting for Task i and Task k to finish
This means that the console application will close before the two Tasks complete
As Andrei Matracaru mentions in his answer, if you add Console.ReadLine(), it will prevent the console application from closing before the strings have been printed to the screen.
A more elegant solution is to use Task.WhenAll. Task.WhenAll will allow the code yield to its calling thread (in this case, the Main Thread) which doesn't freeze the UI of the application, and allows the application to continue to print to the screen until the Tasks have completed. This may seem trivial for a simple console app, but it is crucial to never lock up the Main Thread when building any user-interactive application like mobile or web applications; locking up the Main Thread is what causing apps to "freeze".
Camilo Terevinto's answer above recommends Task.WaitAll. This is also bad practice because it will also freeze the application; Task.WaitAll is not awaitable and cannot not yield to the calling thread.
Print1() and Print2() never yield to the Main Thread
These methods are executing on a background thread, but they don't include an await inside of the for loop
An await is necessary inside of the for loop to allow CPU to continue executing the Main Thread, because Console.Write() can only print to the screen on the Main Thread.
Code
using System;
using System.Threading.Tasks;
namespace ConsoleApplication32
{
internal class Program
{
public static async Task<int> Print1()
{
for (int i = 0; i < 100; i++)
{
Console.Write("x");
await Task.Delay(10);
}
return 1;
}
public static async Task<int> Print2()
{
for (int i = 0; i < 100; i++)
{
Console.Write("y");
await Task.Delay(10);
}
return 1;
}
public static async Task Run()
{
var i = Print1();
var k = Print2();
await Task.WhenAll(i, k);
}
private static void Main(string[] args)
{
Task.Run(async () => await Run()).GetAwaiter().GetResult();
}
}
}
Output
xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxxyxyxyxy
Press any key to continue...
C# 7.1 Alternative
C# 7.1 introduces the ability to have an async Main method. This means that we can tweak the Main method as follows:
...
private static async Task Main(string[] args)
{
await Run();
}
...
Tried your code and works just fine. Probably you think it doesn't work because the console window closes very fast. Add a Console.ReadLine() in your Main:
private static void Main(string[] args)
{
Run();
Console.ReadLine();
}
The problem is that you are not waiting for the tasks to complete, hence the program is terminating (no more synchronous code blocking).
If you want the tasks to run concurrently, do this instead:
public static void Run()
{
Task<int> i = Print1();
Task<int> k = Print2();
Task.WaitAll(i, k);
}
The other way to wait for this tasks to complete, would be to do this:
public static async Task Run()
{
Task<int> i = Print1();
Task<int> k = Print2();
await Task.WhenAll(i, k);
}
public static void Main(string[] args)
{
Run().GetAwaiter().GetResult();
}
Related
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");
}
}
I read some articles how to corectly cancel a task. Most of them describe: "you have to just add ThrowIfCancellationRequested() inside the method that is long running" - and that's it.
Ok - everything is clear but how to do it properly if we cannot modify method?
Please take a look on "DLL" class. I thought about to get current thread and abort it but that operation
is doesn't recommended by most of programmers. So my question is: how to do it properly?
Little example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace LongRunningTask
{
static class ClassWithAccess
{
public static void DoHeavyOperation(CancellationToken ct)
{
for (int i = 0; i < 10000000; i++)
{
Thread.Sleep(500);
ct.ThrowIfCancellationRequested();
Console.WriteLine("inside local method that we can modify");
}
}
}
static class DLL
{
public static void DoHeavyOperation()
{
for (int i = 0; i < 10000000; i++)
{
Thread.Sleep(500);
//ct.ThrowIfCancellationRequested(); we can't do this
Console.WriteLine("inside DLL");
}
}
}
class Program
{
static void Main(string[] args)
{
CancellationTokenSource src = new CancellationTokenSource();
CancellationToken ct = src.Token;
Console.WriteLine("start");
Task.Run(() =>
{
//ClassWithAccess.DoHeavyOperation(ct);
DLL.DoHeavyOperation();
}, ct);
Thread.Sleep(3000);
src.Cancel();
Console.WriteLine("Cancel");
Console.ReadKey();
}
}
}
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);
}
I want to print every 2 sec number but and in the end i get only 0 what i need to do to get this every 2 sec?
result:
0
1
.
.
49
private static void Main(string[] args) {
Send();
}
public static async Task Send() {
for (int i = 0; i < 50; i++) {
Console.WriteLine(i);
await Task.Delay(2000);
}
}
well, simply because your Main method won't wait for the send method to finish and you can't use the await keyword in order for that to happened since the compiler won't allow an async Main, in that case you could simple use a Task.Run
private static void Main(string[] args)
{
Task.Run(async () =>
{
await Send();
}).Wait();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
What you are missing is a Console.ReadLine() call, to halt the console from exiting the program before your task gets executed. Tested the code below and it works.
private static void Main(string[] args)
{
Send();
Console.ReadLine();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
Building on Josephs answer, but in a more general "How to do asynchronous console apps"-way
Using asynchronous code in console apps can be a bit tricky. I generally use a substitute MainAsync method that the original Main() calls and makes a blocking wait on.
Here comes an example of how it could be done:
class Program
{
static void Main(string[] args)
{
Task mainTask = MainAsync(args);
mainTask.Wait();
// Instead of writing more code here, use the MainAsync-method as your new Main()
}
static async Task MainAsync(string[] args)
{
await Send();
Console.ReadLine();
}
public static async Task Send()
{
for (int i = 0; i < 50; i++)
{
Console.WriteLine(i);
await Task.Delay(2000);
}
}
}
This might have a very simple answer, already posted here somewhere, but there is also a lot of old misleading information about threads.
My question is this: How do I prevent my Console from terminating before the background work is complete? Since the Task is created far down in the business layers of my program, I do not have access to it when the Console terminates (not like in this simple example). Is there some way of awaiting all Tasks that have been created within the context of my application?
public class Program
{
private static void Main(string[] args)
{
DoBackgroundWork();
System.Console.WriteLine("Doing something else during background work");
// Wait for all created Tasks to complete before terminating
???
}
private static void DoBackgroundWork()
{
var task = Task.Run(() =>
{
System.Console.WriteLine("Starting background work");
Thread.Sleep(10000);
System.Console.WriteLine("Background work finished!");
});
}
}
Return the task so that you can wait on it.
private static void Main(string[] args)
{
var task = DoBackgroundWork();
System.Console.WriteLine("Doing something else during background work");
// Wait for all created Tasks to complete before terminating
task.Wait();
}
private static Task DoBackgroundWork()
{
var task = Task.Run(() =>
{
System.Console.WriteLine("Starting background work");
Thread.Sleep(1000);
System.Console.WriteLine("Background work finished!");
});
return task;
}