When my .NET code returned a Task, it wasn't doing the expected outcome. Changing it to async/await, fixed...
I have some pretty stock standard code for a typical .NET Core 2+ program.cs
. I'm mainly setting up Logging.
With the code below, it's not async/await
and the following happens:
- Logging occurs during the startup process 100%.
- the
CloseAndFlush();
occurs nearly instantly, before the rest of the app does stuff. - No logging occurs in any controllers.
This is in part due to how Serilog works but that's not really the important bit, AFAIK.
The code steps right over return CreateWebHostBuilder(args).Build().RunAsync();
.. when I thought this is the part where it would be awaited
by the caller? When I debug my app, it does await (hang) on that line ... until the app has completed starting up. Once it's ready to accept connections, it then returns back here ... and runs through .. which means it calls Log.CloseAndFlush()
and then return Task.CompletedTask
.
Buggy Code:
public static Task Main(string args)
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// awaits here until the app has 100% started, then continues.
return CreateWebHostBuilder(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Logger.Fatal(exception, "Host terminated unexpectantly. Sadness :~(");
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
return Task.CompletedTask;
}
Now changing this to async/await
fixes everything. The code does correctly await on return CreateWebHostBuilder(args).Build().RunAsync();
.. and then the CloseAndFlush()
is called when the app is closing/terminating.
public static async Task Main<T>(string args) where T : class
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// Correctly waits here until the app explodes or ctrl-c has been pressed.
await CreateWebHostBuilder<T>(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Stopped program because of an exception.");
}
Log.Debug("Finished shutting down app.");
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
Would returning a Task probably be only suitable if the method is only doing one async/await
in it AND that is the last thing in the method?
c# .net asynchronous async-await
add a comment |
I have some pretty stock standard code for a typical .NET Core 2+ program.cs
. I'm mainly setting up Logging.
With the code below, it's not async/await
and the following happens:
- Logging occurs during the startup process 100%.
- the
CloseAndFlush();
occurs nearly instantly, before the rest of the app does stuff. - No logging occurs in any controllers.
This is in part due to how Serilog works but that's not really the important bit, AFAIK.
The code steps right over return CreateWebHostBuilder(args).Build().RunAsync();
.. when I thought this is the part where it would be awaited
by the caller? When I debug my app, it does await (hang) on that line ... until the app has completed starting up. Once it's ready to accept connections, it then returns back here ... and runs through .. which means it calls Log.CloseAndFlush()
and then return Task.CompletedTask
.
Buggy Code:
public static Task Main(string args)
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// awaits here until the app has 100% started, then continues.
return CreateWebHostBuilder(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Logger.Fatal(exception, "Host terminated unexpectantly. Sadness :~(");
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
return Task.CompletedTask;
}
Now changing this to async/await
fixes everything. The code does correctly await on return CreateWebHostBuilder(args).Build().RunAsync();
.. and then the CloseAndFlush()
is called when the app is closing/terminating.
public static async Task Main<T>(string args) where T : class
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// Correctly waits here until the app explodes or ctrl-c has been pressed.
await CreateWebHostBuilder<T>(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Stopped program because of an exception.");
}
Log.Debug("Finished shutting down app.");
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
Would returning a Task probably be only suitable if the method is only doing one async/await
in it AND that is the last thing in the method?
c# .net asynchronous async-await
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18
add a comment |
I have some pretty stock standard code for a typical .NET Core 2+ program.cs
. I'm mainly setting up Logging.
With the code below, it's not async/await
and the following happens:
- Logging occurs during the startup process 100%.
- the
CloseAndFlush();
occurs nearly instantly, before the rest of the app does stuff. - No logging occurs in any controllers.
This is in part due to how Serilog works but that's not really the important bit, AFAIK.
The code steps right over return CreateWebHostBuilder(args).Build().RunAsync();
.. when I thought this is the part where it would be awaited
by the caller? When I debug my app, it does await (hang) on that line ... until the app has completed starting up. Once it's ready to accept connections, it then returns back here ... and runs through .. which means it calls Log.CloseAndFlush()
and then return Task.CompletedTask
.
Buggy Code:
public static Task Main(string args)
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// awaits here until the app has 100% started, then continues.
return CreateWebHostBuilder(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Logger.Fatal(exception, "Host terminated unexpectantly. Sadness :~(");
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
return Task.CompletedTask;
}
Now changing this to async/await
fixes everything. The code does correctly await on return CreateWebHostBuilder(args).Build().RunAsync();
.. and then the CloseAndFlush()
is called when the app is closing/terminating.
public static async Task Main<T>(string args) where T : class
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// Correctly waits here until the app explodes or ctrl-c has been pressed.
await CreateWebHostBuilder<T>(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Stopped program because of an exception.");
}
Log.Debug("Finished shutting down app.");
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
Would returning a Task probably be only suitable if the method is only doing one async/await
in it AND that is the last thing in the method?
c# .net asynchronous async-await
I have some pretty stock standard code for a typical .NET Core 2+ program.cs
. I'm mainly setting up Logging.
With the code below, it's not async/await
and the following happens:
- Logging occurs during the startup process 100%.
- the
CloseAndFlush();
occurs nearly instantly, before the rest of the app does stuff. - No logging occurs in any controllers.
This is in part due to how Serilog works but that's not really the important bit, AFAIK.
The code steps right over return CreateWebHostBuilder(args).Build().RunAsync();
.. when I thought this is the part where it would be awaited
by the caller? When I debug my app, it does await (hang) on that line ... until the app has completed starting up. Once it's ready to accept connections, it then returns back here ... and runs through .. which means it calls Log.CloseAndFlush()
and then return Task.CompletedTask
.
Buggy Code:
public static Task Main(string args)
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// awaits here until the app has 100% started, then continues.
return CreateWebHostBuilder(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Logger.Fatal(exception, "Host terminated unexpectantly. Sadness :~(");
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
return Task.CompletedTask;
}
Now changing this to async/await
fixes everything. The code does correctly await on return CreateWebHostBuilder(args).Build().RunAsync();
.. and then the CloseAndFlush()
is called when the app is closing/terminating.
public static async Task Main<T>(string args) where T : class
{
Log.Logger = new LoggerConfiguration()....CreateLogger();
try
{
// Correctly waits here until the app explodes or ctrl-c has been pressed.
await CreateWebHostBuilder<T>(args).Build().RunAsync();
}
catch (Exception exception)
{
Log.Error(exception, "Stopped program because of an exception.");
}
Log.Debug("Finished shutting down app.");
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
Log.CloseAndFlush();
}
Would returning a Task probably be only suitable if the method is only doing one async/await
in it AND that is the last thing in the method?
c# .net asynchronous async-await
c# .net asynchronous async-await
asked Nov 23 '18 at 8:08
Pure.KromePure.Krome
44.9k90317522
44.9k90317522
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18
add a comment |
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18
add a comment |
2 Answers
2
active
oldest
votes
Returning a Task
1 instead of await
ing it is your way of saying "there's nothing left for this method to do here"2.
If that's not the case (as here, where you don't want your finally clause to run yet), you're not done. You need some way of running some code at a later point in time, but you've got nothing useful to do now. And that's precisely what await
allows you to signal.
1As Jonas mentions, this is exactly true for non-Task
returns too.
2I'm ignoring the case when you take a Task
from elsewhere, add a ContinueWith
and return the resulting Task
, because there you're using a different mechanism to ensure you get to run code later and it's not precisely "this method" that runs in the continuation.
add a comment |
Would returning a Task probably be only suitable if the method is only doing one async/await in it AND that is the last thing in the method?
Basically, yes. Returning the task as in your first snippet will let the finally block run as you leave the scope, just like any other return statement inside a try with an associated finally. And as commented by ckuri your catch block won't do what you want either, as it will not catch exceptions from the task, only from the setup in CreateWebHostBuilder
itself.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53442791%2fwhen-my-net-code-returned-a-task-it-wasnt-doing-the-expected-outcome-changin%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Returning a Task
1 instead of await
ing it is your way of saying "there's nothing left for this method to do here"2.
If that's not the case (as here, where you don't want your finally clause to run yet), you're not done. You need some way of running some code at a later point in time, but you've got nothing useful to do now. And that's precisely what await
allows you to signal.
1As Jonas mentions, this is exactly true for non-Task
returns too.
2I'm ignoring the case when you take a Task
from elsewhere, add a ContinueWith
and return the resulting Task
, because there you're using a different mechanism to ensure you get to run code later and it's not precisely "this method" that runs in the continuation.
add a comment |
Returning a Task
1 instead of await
ing it is your way of saying "there's nothing left for this method to do here"2.
If that's not the case (as here, where you don't want your finally clause to run yet), you're not done. You need some way of running some code at a later point in time, but you've got nothing useful to do now. And that's precisely what await
allows you to signal.
1As Jonas mentions, this is exactly true for non-Task
returns too.
2I'm ignoring the case when you take a Task
from elsewhere, add a ContinueWith
and return the resulting Task
, because there you're using a different mechanism to ensure you get to run code later and it's not precisely "this method" that runs in the continuation.
add a comment |
Returning a Task
1 instead of await
ing it is your way of saying "there's nothing left for this method to do here"2.
If that's not the case (as here, where you don't want your finally clause to run yet), you're not done. You need some way of running some code at a later point in time, but you've got nothing useful to do now. And that's precisely what await
allows you to signal.
1As Jonas mentions, this is exactly true for non-Task
returns too.
2I'm ignoring the case when you take a Task
from elsewhere, add a ContinueWith
and return the resulting Task
, because there you're using a different mechanism to ensure you get to run code later and it's not precisely "this method" that runs in the continuation.
Returning a Task
1 instead of await
ing it is your way of saying "there's nothing left for this method to do here"2.
If that's not the case (as here, where you don't want your finally clause to run yet), you're not done. You need some way of running some code at a later point in time, but you've got nothing useful to do now. And that's precisely what await
allows you to signal.
1As Jonas mentions, this is exactly true for non-Task
returns too.
2I'm ignoring the case when you take a Task
from elsewhere, add a ContinueWith
and return the resulting Task
, because there you're using a different mechanism to ensure you get to run code later and it's not precisely "this method" that runs in the continuation.
answered Nov 23 '18 at 8:23
Damien_The_UnbelieverDamien_The_Unbeliever
195k17248335
195k17248335
add a comment |
add a comment |
Would returning a Task probably be only suitable if the method is only doing one async/await in it AND that is the last thing in the method?
Basically, yes. Returning the task as in your first snippet will let the finally block run as you leave the scope, just like any other return statement inside a try with an associated finally. And as commented by ckuri your catch block won't do what you want either, as it will not catch exceptions from the task, only from the setup in CreateWebHostBuilder
itself.
add a comment |
Would returning a Task probably be only suitable if the method is only doing one async/await in it AND that is the last thing in the method?
Basically, yes. Returning the task as in your first snippet will let the finally block run as you leave the scope, just like any other return statement inside a try with an associated finally. And as commented by ckuri your catch block won't do what you want either, as it will not catch exceptions from the task, only from the setup in CreateWebHostBuilder
itself.
add a comment |
Would returning a Task probably be only suitable if the method is only doing one async/await in it AND that is the last thing in the method?
Basically, yes. Returning the task as in your first snippet will let the finally block run as you leave the scope, just like any other return statement inside a try with an associated finally. And as commented by ckuri your catch block won't do what you want either, as it will not catch exceptions from the task, only from the setup in CreateWebHostBuilder
itself.
Would returning a Task probably be only suitable if the method is only doing one async/await in it AND that is the last thing in the method?
Basically, yes. Returning the task as in your first snippet will let the finally block run as you leave the scope, just like any other return statement inside a try with an associated finally. And as commented by ckuri your catch block won't do what you want either, as it will not catch exceptions from the task, only from the setup in CreateWebHostBuilder
itself.
answered Nov 23 '18 at 8:20
Jonas HøghJonas Høgh
7,21311737
7,21311737
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53442791%2fwhen-my-net-code-returned-a-task-it-wasnt-doing-the-expected-outcome-changin%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Generally speaking, yes, passing through a task only makes sense if your other method code doesn't depend on the outcome of the task which often will be the end of the method. However, you need to keep in mind that exceptions will only be raised when the task is awaited. Thus, if a task is passed through methods A and B, and only method C awaits, the exception stack trace will not contain A and B which might complicate debugging.
– ckuri
Nov 23 '18 at 8:18