C
C#•3y ago
Thinker

When to use ValueTask?

Despite having used C# for a while I'm... not that familiar with async. I know people sometimes say to sometimes use ValueTask<T> instead of Task<T>, but when should you exactly do this? The extent of my knowledge (and reading the docs) is that ValueTask<T> is like a DU between either a T value or a Task<T>, so is it ideal for situations in which an async method may just return a cached value?
37 Replies
Thinker
ThinkerOP•3y ago
And since you can use ValueTask<T> as the result of an async method, is the compiler smart enough to generate the appropriate code based on whether such a cached value is used without any async calls being used?
canton7
canton7•3y ago
It's more that it's a cheaper alternative to Task, but more limited
Thinker
ThinkerOP•3y ago
Mind elaborating?
canton7
canton7•3y ago
It doesn't allocate (as much, at least), but you can't do things like await it twice, or use it with e.g. Task.WhenAll
Klarth
Klarth•3y ago
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Thinker
ThinkerOP•3y ago
If I essentially have this
public async Task<string> GetStringAsync() {
if (cachedValue is not null) return cachedValue;

string value = await DoSomeOtherOperationAsync();
cachedValue = value;
return value;
}
public async Task<string> GetStringAsync() {
if (cachedValue is not null) return cachedValue;

string value = await DoSomeOtherOperationAsync();
cachedValue = value;
return value;
}
Is it worth using ValueTask?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Thinker
ThinkerOP•3y ago
Nice
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Thinker
ThinkerOP•3y ago
Why would you await it twice anyway?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX•3y ago
canton7#1569
In that case, it's normal to cache the task. That way, if someone queries the value while it's still being loaded by a previous query, that won't result in the value being calculated a second time in parallel: instead the second caller gets given the same Task which was given to the first caller, which will complete when the value becomes available. If the value is already fetched, awaiting the task just completes synchronously and gives the value straight away
Quoted by
<@!660066004059029524> from #advanced (click here)
React with ❌ to remove this embed.
canton7
canton7•3y ago
public Task<string> GetStringAsync() {
if (cachedValue is not null) return cachedValue;

string value = DoSomeOtherOperationAsync();
cachedValue = value;
return value;
}
public Task<string> GetStringAsync() {
if (cachedValue is not null) return cachedValue;

string value = DoSomeOtherOperationAsync();
cachedValue = value;
return value;
}
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Thinker
ThinkerOP•3y ago
Assuming you mean return Task.FromResult(cachedValue); But that makes sense
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
Thinker
ThinkerOP•3y ago
An already completed task?
Unknown User
Unknown User•3y ago
Message Not Public
Sign In & Join Server To View
canton7
canton7•3y ago
Well, it'll complete when it completes. That's the point
Kuinox
Kuinox•3y ago
My own rules are: I know the async func will almost never complete synchronously => Task If I know it will return synchronously most of the time => ValueTask if it's internal function and I already didn't decided with 2 previous point => ValueTask
canton7
canton7•3y ago
No. cachedValue is a Task<string> (otherwise none of the other lines would compile) The point is that if someone calls GetStringAsync (which calls DoSomeOtherOperationAsync) you immediately cache the work-in-progress task. Then if someone else calls GetStringAsync before the task has completed, you give them back the same Task rather than calling DoSomeOtherOperationAsync a second time. If the task has completed, great, awaiting it is a free noop My view here is that single allocations are staggeringly cheap, and bugs are expensive. I'll always go with Task unless it's a high-perf scenario, in which case I'll have my profiler out and it can tell me if I need to move to ValueTask
Akseli
Akseli•3y ago
🤨 cachedValue is a string not Task<string> if you are directly awaiting the task var value = await GetStringAsync(); use a valuetask if you need to use the task var task = GetStringAsync(); // await task later then use a normal task
shua
shua•3y ago
@thinker227 - relevant, not sure if anyone mentioned this but 1) - As such, the default choice for any asynchronous method should be to return a Task or Task<TResult> (https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=net-6.0) 2) Microsoft uses a ValueTask in the ProtectedBrowserStorage class in ASP.Net core for the method that is called to retrieve cached values... I think you mentioned something about cacheing so just wanted to point out that is how MSFT is using it in this case.
ValueTask Struct (System.Threading.Tasks)
Provides a value type that wraps a Task and a TResult, only one of which is used.
Kiel
Kiel•3y ago
from my own personal asking around and seeing other people converse on the topic: use ValueTask if you expect your method to return synchronously a greater-than-negligible amount of times. Use Task otherwise. ValueTask can, to my knowledge, offer a performance/speed gain over Task (if the method performs synchronous work only), but this is only noticible at massive scales where your method is called a LOT. it's not worth fretting over otherwise
shua
shua•3y ago
Another funny thing is that Microsoft is calling methods SetAsync when they are not marked with a async modifier which is pretty bizarre..
Klarth
Klarth•3y ago
A modern API? Not an older EAP/APM API?
Akseli
Akseli•3y ago
difference between task and valuetask is that task allocates a new object on every call but valuetask only allocates a new object if the operation was asynchronous otherwise the call is allocation free
canton7
canton7•3y ago
I'm saying turn it into a Task<string>
qqdev
qqdev•3y ago
Do they return a Task maybe?
shua
shua•3y ago
No, they return a ValueTask… and it isn’t marked with async so if something bizarre happens they are tying up the whole thread… they also suffix the method with Async so it looks like a Async method but it isn’t.. I’m sure though there is something I’m missing because this seems really bad. If you go to the .NET open source and type in the search ProtectedBrowserStorage you will see it. I’m on a phone, otherwise I would send the link
Klarth
Klarth•3y ago
https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Components/Server/src/ProtectedBrowserStorage/ProtectedBrowserStorage.cs#L59 so these are mostly passthrough methods where async is ellided for perf. I assume MS knows how to do that properly...tagging the method names with Async can be confusing.
qqdev
qqdev•3y ago
^ This is what I thought when reading your message I think this is totally fine
Thinker
ThinkerOP•3y ago
how do you mark a thread as done catsweat
Kuinox
Kuinox•3y ago
right click on it => archive
Thinker
ThinkerOP•3y ago
I figured that out and then yall posted again ._.

Did you find this page helpful?