This question already has an answer here:
async Task vs async void
(1 answer)
Closed 3 years ago.
How would I make the main thread wait until DisplayAdd has displayed the output? If I add a Console.Read() at the end, everything works but is there another way to tell the main thread to wait until Calculate() has finished?
namespace TestDelegate
{
public class Add
{
public delegate void SendResult(int i);
public SendResult WhereToSend;
public async void Calculate (int number)
{
Console.WriteLine("Entered");
int result = number + number;
await Task.Delay(4000);
WhereToSend (result);
// Console.Read();
}
}
}
namespace TestStuff
{
class Program
{
static void Main(string[] args)
{
Add obj = new Add();
Console.WriteLine("Started Calculating");
obj.Calculate(10);
obj.WhereToSend = DisplayAdd;
}
static void DisplayAdd(int value)
{
Console.WriteLine(value);
}
}
}
You can define the delegate as Task return type (awaitable type). With this the method will finish before main thread terminates.
namespace TestDelegate
{
public delegate Task SendResult(int i);
public class Add
{
public SendResult WhereToSend;
public async Task Calculate (int number)
{
Console.WriteLine("Entered");
int result = number + number;
await WhereToSend (result);
}
}
}
namespace TestStuff
{
class Program
{
static void Main(string[] args)
{
Add obj = new Add();
obj.WhereToSend = DisplayAdd;
Console.WriteLine("Started Calculating");
obj.Calculate(10).Wait();
}
static async Task DisplayAdd(int value)
{
// Some awaitable operation like below as per your business logic
await Task.Delay(1);
Console.WriteLine(value);
}
}
}
In above program, I've changed the definition of Calculate method to async Task so that it can be marked for Waitable. The async void method are primarily used for UI events hanlder or fire and forget method.
Please check this dotnetfiddle which demonstrates the scenario.
Related
This question already has answers here:
Why is the console window closing immediately once displayed my output?
(15 answers)
Closed 1 year ago.
I wanted to test if I can trigger an event using method working in a new task.
when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
The output is nothing!
However, when I do this:
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public void method2()
{
//Here is the change
method1();
Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
the output is this:
The threshold was reached
The threshold was reached
this is strange!
I could not understand why it prints twice.
However, I excepted it will not work with method1 is running in new task, because it will not be working in the same thread
could someone explain why?
and is there a way to communicate a concurrent method with the parent thread?
thanks in advance
As mentioned in comment, your code finishes before the task will run and pring anything. You should modify your code in async manner to await the task to finish
using System;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
subscriber f = new subscriber();
await f.Test();
}
}
class subscriber
{
publisher x;
public subscriber()
{
x = new publisher();
x.ThresholdReached += c_ThresholdReached;
}
public async Task Test()
{
await x.method2();
}
static void c_ThresholdReached(object sender, EventArgs e)
{
Console.WriteLine("The threshold was reached.");
}
}
class publisher
{
public event EventHandler ThresholdReached;
public publisher()
{
}
public void method1()
{
OnThresholdReached(EventArgs.Empty);
}
public async Task method2()
{
await Task.Run(() => method1());
}
protected virtual void OnThresholdReached(EventArgs e)
{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}
}
}
As for your second snippet, that looks like the Task has time to run and pring while the main thread printing inside method1. Some times it will pring twice (if have enaught of time) or will print once (if Console.Write in main thread will finish before the task will run).
Additional note: please be aware that your event handler will be called not in main thread, but in the thread in which task is executed (typically, this will be a thread from a thread pool)
When you call Task.Run(...), the action inside Run() will be started in new thread.
In your first case you start the new thread with Task.Run(), but the program finishes and exits before it executes that the new thread.
You can prevent the main thread from terminating by adding Console.Read() at the end of Main(). This way you can wait for the second thread to finish.
class Program
{
static void Main(string[] args)
{
subscriber f = new subscriber();
Console.Read();
}
}
In your second case you will get two messages because of this:
public void method2()
{
method1();
Task.Run(() => method1());
}
This time since you are directly calling method1() you will for sure write the message once in the console. Since printing on the console (any IO in general) is relatively slow, the second thread (created by Task.Run()) will have time to execute, and print the message second time.
Another solution is to create the thread manually
public void method2()
{
Thread t1 = new Thread(() => method1());
t1.IsBackground = false;
t1.Start();
}
The program will not exit until all foreground threads have finished. So when you set IsBackground = false;, the program will wait for that thread
I have an EF Core program.
Code
class EagerLoading
{
public async void load()
{
Console.WriteLine("a");
var teams = await loadTeamsAsync();
Console.WriteLine("c");
}
public async Task<List<Team>> loadTeamsAsync()
{
Console.WriteLine("b");
using (var context = new SchoolContext())
{
return await context.teams.
ToListAsync();
}
}
}
//Entry method
static void Main(string[] args)
{
new EagerLoading().load();
}
Output
a
b
Expected
a
b
c
Question
where am I wrong? why the Console.WriteLine("c"); doesn't execute?
You will need to put a ReadKey or similar in the Main method to stop the application from exiting.
Load is an async void (which should be ringing alarm bells) and runs unobserved, in-turn there is a high probability of the application exiting before it finishes in the way you expect (or in this case writing to the console).
static void Main(string[] args)
{
new EagerLoading().load();
Console.ReadKey();
}
Note : You should only ever be using on async voids on event handlers (or constructs that logically represent event handlers), it's more-or-less the primary reason they exist.
I would have a async call to a function as following:
I'll call a method from the main method, this function should be async, how I do this?
A little example:
private static void Main(string[] args)
{
StartDoingNothingAsync();
Console.WriteLine("test");
Console.Read();
}
private static async void StartDoingNothingAsync()
{
for (var i = 0; i < 5000; i++)
{
//do something
}
Console.WriteLine("leaved");
}
I would first the output "test", before "leaved", how can I practice this?
The simplest option would be to introduce a delay into your async method:
private static async void StartDoingNothingAsync()
{
await Task.Delay(1000);
// This will be called on a thread-pool thread
Console.WriteLine("leaved");
}
That won't guarantee that test will be printed before leaved, but it seems very likely. If you actually want a guarantee, you'd have to pass something into StartDoingNothingAsync that you then signal after printing test... but it's not really clear what you're trying to achieve.
Note that async methods should almost never be void - basically that's only available to allow for async event handlers. Make the method return Task instead - that way your calling code can tell when it's completed. (Even if you don't use that fact in this case, it's a good habit to get into.)
Now that we have a bit more information about what you're trying to achieve, I would recommend not making StartDoingNothingAsync an async method - just use a task instead:
private static void Main(string[] args)
{
Task task = Task.Run(DoSomething);
Console.WriteLine("test");
task.Wait();
Console.Read();
}
static void DoSomething()
{
// Code to run on a separate thread
}
You can do it like this
private static void Main(string[] args)
{
StartDoingNothingAsync();
Console.WriteLine("test");
Console.Read();
}
private static async void StartDoingNothingAsync()
{
await Task.Run(async delegate()
{
for (var i = 0; i < 5000; i++)
{
//do something
}
Console.WriteLine("leaved");
});
}
You can use Task for that. In that case you don't need to mark your function as async:
private static void Main(string[] args)
{
new Task(StartDoingNothing).Start();
Console.WriteLine("test");
Console.Read();
}
private static void StartDoingNothing()
{
for (var i = 0; i < 5000; i++)
{
//do something
}
Console.WriteLine("leaved");
}
Why in the second case the exception is thrown?
async void f() {
await media.InitializeAsync();
isInit = true;
capturePreview.Source = media;
await media.StartPreviewAsync(); // Ok
}
// ------------------------------------------
void f() {
StaticClass.g(capturePreview.Source);
}
public static class StaticClass {
public static async void g(MediaSource source) {
await media.InitializeAsync();
isInit = true;
source = media;
await media.StartPreviewAsync(); // Unknown Exception (It's seems that media isn't init)
}
}
The f() function use a function which include async.
So I think the f() should be signed async as well.
Like this: void async f(){...}
If you really want media to be initialized, why don't you do the rest of the code in the .done function ?
void f() {
StaticClass.g(capturePreview.Source);
}
public static class StaticClass {
public static async void g(MediaSource source) {
media.InitializeAsync().done(
isInit = true;
source = media;
await media.StartPreviewAsync(););
}
}
Even if I'm not fully sure you can do an async within a done callback.
I have a console application that essentially looks like this
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();
}
}
class DoStuffService
{
public void Start()
{
Task.Factory.StartNew(() => { LongRunningOperation() });
}
private void LongRunningOperation()
{
// stuff
}
}
What's the best way these days to ensure my console application doesn't exit before LongRunningOperation() is complete, and also allows me a way to be notified in the console application (for logging purposes for instance) that LongRunningOperation() is complete.
call Wait() on the task. For example:
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();
// stuff...
svc.DelayTilDone();
}
}
public class DoStuffService
{
Task _t;
public void Start()
{
_t = Task.Factory.StartNew(() => { LongRunningOperation(); });
}
public void DelayTilDone()
{
if (_t==null) return;
_t.Wait();
}
private void LongRunningOperation()
{
System.Threading.Thread.Sleep(6000);
System.Console.WriteLine("LRO done");
}
}
In addition to Cheeso's answer, you'll want to handle Console.CancelKeyPress so that you can display a busy message and set e.Cancel = True.
There's nothing you can do to prevent them from killing the process, but you can at least handle Ctrl+C and Ctrl+Break.
There is a similar thread C# multi-threaded console application - Console quits before threads complete
You can simply return a started task and Wait() or ContinueWith() on it:
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start().Wait();//bool res = svc.Start()
Trace.WriteLine("333333333333333");
}
}
public class DoStuffService
{
public Task Start()
{
return Task.Factory.StartNew
(() =>
{
Trace.WriteLine("111111111");
LongRunningOperation(); ;
});
}
private void LongRunningOperation()
{
System.Threading.Thread.Sleep(3000);
Trace.WriteLine("2222222222");
}
}
A task will block the parent thread until completion, if to access its Result property, so:
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
DoStuffService svc = new DoStuffService();
svc.Start();//bool res = svc.Start()
Trace.WriteLine("333333333333333");
}
}
public class DoStuffService
{
public Task<bool> MyTask;
public bool Start()
{
MyTask = Task.Factory.StartNew<bool>
(() =>
{
Trace.WriteLine("111111111");
return LongRunningOperation();;
});
return MyTask.Result;
}
private bool LongRunningOperation()
{
System.Threading.Thread.Sleep(3000);
Trace.WriteLine("2222222222");
return true;
}
}