Replies: 97 comments 97 replies
-
A helper method could cut down on the typing too. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I don't see how, since you'd have to make that an awaitable and then call Unless I'm missing something? Additionally, with awaitable being a pattern, you'd have to create a new extension (assuming this is viable) for each type that is awaitable (see the new |
Beta Was this translation helpful? Give feedback.
-
@casperOne |
Beta Was this translation helpful? Give feedback.
-
Technically C# knows nothing about A helper method could return the I'd prefer an assembly-targeted attribute over some new keyword which would have to be applied everywhere. For reference: https://github.com/dotnet/roslyn/issues/7673 |
Beta Was this translation helpful? Give feedback.
-
@Vlad-Stryapko With C# 7.0, you can await anything that is "Task-like"; this proposal targets those items that are "Task-like." @HaloFour While an attribute would handle this, I don't like the precedent it sets; I can't think of any other attribute like this which alters how the compiler handles a keyword/section of code. The closest thing I can think of is the compiler flag for checked/unchecked overflow checking. While I know there are other cases where the line of code is subject to interpretation based on contextual information that's not so obvious reading the line of code in question, I'd prefer that we go down those paths as infrequently as possible. I imagine there would be a tremendous amount of pain having to remember whether or not that assembly is set all the time (and then having to fish for it, since it can be in any code file in the assembly). The referenced Roslyn issue does round up the pain points well. |
Beta Was this translation helpful? Give feedback.
-
You could always What C# 7.0 does is allow for My issue with something like another keyword is that it involves a language change for a BCL concern and that it still requires the author to remember to apply it all over the place. An async-capable library is likely going to apply this logic to literally every public static class TaskExtensions {
public static ConfiguredTaskAwaiter NoSync(this Task task) => task.ConfigureAwait(false);
public static ConfiguredTaskAwaiter<T> NoSync<T>(this Task<T> task) => task.ConfigureAwait(false);
} Then you can use: var r1 = await task.NoSync(); |
Beta Was this translation helpful? Give feedback.
-
Reducing typing is one of the goals. I don't agree that there's a "correct way [to handle this] by default." It's contextual. I do agree that when I see how the extension can work now, thank you for that. However, if we adopt your goal of "handling it correctly by default", then the extension method suffers the same issue that a new keyword does, you have apply it everywhere. |
Beta Was this translation helpful? Give feedback.
-
@ufcpp advocated an interesting approach in a related discussion: dotnet/roslyn#7673. The idea is to change the async method to return a different Task-like type that encapsulates ConfigureAwait(false). To be sure, this still means that manual intervention is necessary somewhere to opt-in to this behavior. |
Beta Was this translation helpful? Give feedback.
-
@MattDillard I don't like that that approach "leaks" to the signature of the method. It should be an implementation detail, not something that's visible from the outside. |
Beta Was this translation helpful? Give feedback.
-
I don't quite agree with that. The only reason for ConfigureAwait(false) is to prevent single-thread synchronization context from deadlock, which is the case for GUI frameworks like WinForm/WPF. I don't know why you need it on the server side. As per my understanding, the only case for ConfigureAwait(false) is for a library which may be consumed in WinForm/WPF. And even in this case, you don't need ConfigureAwait(false) everywhere. You only need it in [2017-12-22] Update for later readers. In legacy ASP.NET, deadlock will definitely happen if caller code try to get result synchronously e.g. by |
Beta Was this translation helpful? Give feedback.
-
One reason is that the synchronization context in ASP.NET is also single-threaded (even though it's not bound to a specific thread), so you still need A second reason is performance: restoring the context is not free, so if you care about performance and you don't need the context, it's wasteful to restore it.
That's not how And it's not limited to those "first public async Task<string> GetData() => await GetA() + await GetB();
private async Task<string> GetA() => await GetFile("a.txt");
private async Task<string> GetB() => await GetFile("b.txt");
private async Task<string> GetFile(string name)
{
using (var reader = new StreamReader(name))
{
return await reader.ReadToEndAsync();
}
} Where do you think it's safe to omit And even if you do have the guarantee of being detached now, a seemingly innocuous code change can break it, so I think you should use * Note that the code does not correctly set |
Beta Was this translation helpful? Give feedback.
-
I don't think there is deadlock issue with ASP.NET:
Context restore is pretty lightweight, which is just a few assignments, which is comparable to the call to ConfigureAwait().
No, I didn't say IMO, the core issue is the massive confusion caused by ConfigureAwait(false), and the "best practices" built atop the confusion. |
Beta Was this translation helpful? Give feedback.
-
That's not true until after the first await that the inside function performs. Async methods always start out executing synchronously with the SynchronizationContext still in place. |
Beta Was this translation helpful? Give feedback.
-
hmm... i'm confused too... it has to be public Task<string> GetData() => Task.Run(async () => await GetA() + await GetB()); or use a helper function like public async Task<string> GetData()
{
await AsyncHelper.LeaveSynchronizationContext(); // which calls ConfigureAwait(false) inside
return await GetA() + await GetB();
} |
Beta Was this translation helpful? Give feedback.
-
Or just public async Task<string> GetData()
{
return await GetA().ConfigureAwait(false) + await GetB();
} ;-) (The downside of AsyncHelper.LeaveSynchronizationContext is that it would have to await Task.Yield() or in some other way introduce an unnecessary context switch. Things that could have returned synchronously will now be forced to execute slower.) |
Beta Was this translation helpful? Give feedback.
-
@jjxtra In addition to the reasons @CyrusNajmabadi mentioned, considering the discriminator isn't between client/server libraries, but where callbacks need access to synchronization contexts (or not), a global setting like that doesn't have the granularity needed to address the scenarios currently described in this issue. |
Beta Was this translation helpful? Give feedback.
-
I don't like So I came up with a simple aliasing and yes I know this has downsides, too. Another package (or code file) to add, a namespace to import and maybe more. However, this shows the intent the developer had while the orignal design of So here are my thoughts and my "solution" if you want to call it that way. I'd be happy to know what you think and maybe even see these methods in other projects one day. |
Beta Was this translation helpful? Give feedback.
-
as a halfway house, would it be possible to annotate a class, just like .ConfigureAwait(bool) is implementation dependent, the language would consider the implementation of [AwaitContextFree] to be implementation dependent. It'd be best if there was some way for the code generator or compiler to know what to do? As an implementation note the standard implementation could provide an interface IAsyncConfiguration {ConfigureAwait(bool)} and have Task implement it, and use that as a hook for this action. It would depend on if we could make this a 0 cost abstraction. |
Beta Was this translation helpful? Give feedback.
-
Thinking about how new pragmas like It could be a new preprocessor keyword/directive in its own right as Options would be:
As with other If
|
Beta Was this translation helpful? Give feedback.
-
I don't know were it was mentioned, but I'm now using a custom Task implementation that always returns my own Awaiter that does what I want. suspend fun Library() = taskWithCorrectContext {
// just call suspend functions, the IDE puts an icon,
// this is where C# is better, await is explicit, ironic isn't
} which is way better than void Task Library() {
//lots of configureawait(false)
} The best I could do was. void CustomTask<Something> Library() {
//just await anything from your library of from the CLR, which already has the correct behavior.
} I think the only real solution is dropping C# in favor of F#, you don't lose much, besides some refactor support in the IDE.. fun Library = async {
// I wish the async was like this on C#,
// perhaps we need Computation Expressions in C# !
let! = anythingAsync()
//this is the best pattern ever, its also much cheaper
// than Task for CPU bound things, not only I/O.
} |> Async.StartAsTask() F# does the Of course nothing impedes you from having: fun Library = asyncTask {
let! = anythingAsync()
} //this is a Task
// indeed that was my final solution to the problem So what I propose is adding |
Beta Was this translation helpful? Give feedback.
-
To solves adding new keyword, instead why not using Task<int> Sum(int a, int b) => Task.FromResult(a + b);
int num = await! Sum(1, 2) // first call will invoke .ConfigureAwait(false)
+ await@ Sum(3, 4) // second call with invoke .ConfigureAwait(true)
+ await Sum(2 + 1); // regular call.
Console.WriteLine("Sum of values is {0}", num); Or can be added on end of method call. int num = await Sum(1, 2)! // first call will invoke .ConfigureAwait(false)
+ await Sum(3, 4)!! // second call with invoke .ConfigureAwait(true)
+ await Sum(2 + 1); // regular call. For method call at end can be Or instead of when is int num = await! Sum(1, 2) // after keyword, first call will invoke .ConfigureAwait(false)
+ await Sum(3, 4)! // at end, second call with invoke .ConfigureAwait(true)
+ await Sum(2 + 1); // regular call. if both after Also adding |
Beta Was this translation helpful? Give feedback.
-
Just throwing the idea out there, but what if For example: await(false) SomeAsyncOperation();
// equivalent to:
await SomeAsyncOperation().ConfigureAwait(false); This would allow custom awaitables to also provide their own |
Beta Was this translation helpful? Give feedback.
-
I don't understand how this issue has not even been triaged in an LDM 5 years since being suggested. This is one of the ugliest warts in C#. I can't think of another language where such a critical language feature that developers use dozens of times daily is so crippled as to require such a verbose workaround at every usage. For developers in the real world writing real code, this feature would be 100X more impactful than 90% of the stuff LDT spends time on. And it's not exactly like its controversial or difficult to implement. Guys, let's get this done already. |
Beta Was this translation helpful? Give feedback.
-
What about await! or @await or something similar? |
Beta Was this translation helpful? Give feedback.
-
What about to use proposal for reduce null checks verbosity #2145 in await? Like For |
Beta Was this translation helpful? Give feedback.
-
Rather than playing with
The later will be the default if not defined in csproj, so no breaking existing projects. For new projects, each project template can have a sensible default. |
Beta Was this translation helpful? Give feedback.
-
Another way, global toplevel exposed to set ConfigureAwait on default task scheduler for all taks (and ability to set in inner task schedulers)
Another option pragmas (like previously said): #configureawait false
// all await calls will use .ConfigureAwait(false)
#configureawait restore Or leave |
Beta Was this translation helpful? Give feedback.
-
I create a series of extension methods named "DefaultAwait()" that are specific for that assembly's implementation and call either For example:
|
Beta Was this translation helpful? Give feedback.
-
I would like to propose something much simpler. Since the As
That would also allow the compiler team to make further optimizations to the rendered code since you're implicitly and statically defining that you do not want the I see I want to await my task, I want to await my task but I want to configure it as false, It also adds only a single byte to each method. 😉 |
Beta Was this translation helpful? Give feedback.
-
This thread certainly saved me! The whole async/await pattern is great except it was probably built with "let me be helpful managing the context switching for you" - with no regard to the various environments/libraries it may have to be used in. The ConfigureAwait pattern just seems like an after thought. Is there a facility for dependency injection or overrides to have your own custom context in your library through a Provider pattern? |
Beta Was this translation helpful? Give feedback.
-
Problem
When not working with a
SynchronizationContext
, it's recommended that you useConfigureAwait(false)
to indicate that a SynchronizationContext is not required on return from the awaitable.Currently, the C# compiler always captures
SynchronizationContext
when callingawait
(without an explicitConfigureAwait
directive); this is a reasonable default when doing work close to the user interface or servicing the request in ASP.NET (the most prevalent uses for the default/ConfigureAwait(true)
).However, when working on the server side or in libraries, using
ConfigureAwait(false)
is nearly ubiquitous, and leads to a tremendous amount of repetitive typing.Proposed Solution
A new contextual keyword that is used in conjunction with, or as a replacement to
async
.I currently don't have a good idea of what that keyword should be, so for now, let's use
foo
as a placeholder.When applied to anything that implements the awaitable pattern, it assumes
ConfigureAwait(false)
is attached to the result.Examples
Notes
I've looked at the existing list of C# keywords and contextual keywords, but I can't find anything that makes sense to use for this case. It may require a new keyword/contextual keyword, but if something existing makes sense, then by all means, let's use that.
The point being, many developers tend to do a lot of library/server side work, and having to type
ConfigureAwait(false)
(even with Intellisense) is a lot of wasted energy.Beta Was this translation helpful? Give feedback.
All reactions