-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsyncLock #28194
Comments
|
@ichensky - The primary benefit of Note, however, that although an async lock is useful/necessary in some situations, blind use makes it really easy to lead to deadlocks. For instance, if you have another async call inside the scope of an |
@Clockwork-Muse I'd argue any locking can easily lead to deadlocks. Or race conditions. Obviously, its far better to avoid writing to shared data structures with multi-threaded code, and if you do, use a framework type. But sometimes that's just not possible and you need to lock on something. |
Oh, no, I agree, sometimes you really need to do something like that. For instance, I was messing with something that was doing multi-threaded writes to a shared TCP connection (in response to received messages, no less). It's just that the way continuations work means that the potential for deadlock is much higher, because of the assumption of safety, and the types of resources being accessed. |
It would be nice to add asynchronous version of I would like to propose ready-to-use implementations of these locks as a proof of concept. AsyncExclusiveLock, AsyncReaderWriteLock, AsyncManualResetEvent and AsyncAutolResetEvent are asynchronous versions of var readerWriterLock = new AsyncReaderWriteLock();
using(await readerWriterLock.AcquireReadLock(CancellationToken.None))
{
//reader
}
using(await readerWriterLock.AcquireWriteLock(TimeSpan.FromSeconds(1))
{
//writer
}
using(await readerWriterLock.AcquireUpgradeableLock(TimeSpan.FromSeconds(1), CancellationToken.None)
{
//code protected by upgradeable lock
}
var exclusiveLock = new AsyncExclusiveLock();
using(await exclusiveLock.AcquireLock(CancellationToken.None))
{
//code protected by mutually exclusive lock
}
var sem = new SemaphoreSlim(0, 42);
using(await sem.AcquireLockAsync(CancellationToken.None))
{
} During the implementation I found that usage of According with official doc:
It means that a) Asynchronous lock acquisition methods in |
The advantage of
|
Any news? |
After this long time passed since adding tasks, I want to add a vote to this issue to have synchronization primitives in task-based codes. |
No updates? In the meantime I've just been using a small modification of Stephen Toub's version based around public class AsyncLock
{
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
private readonly Task<IDisposable> releaser;
public AsyncLock()
{
releaser = Task.FromResult((IDisposable)new AsyncLockReleaser(this));
}
public Task<IDisposable> LockAsync(CancellationToken cancellationToken)
{
Task wait = semaphore.WaitAsync(cancellationToken);
return wait.IsCompletedSuccessfully
? releaser
: wait.ContinueWith(
(_, state) => (IDisposable)state!,
releaser.Result,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
private sealed class AsyncLockReleaser : IDisposable
{
private readonly AsyncLock toRelease;
internal AsyncLockReleaser(AsyncLock toRelease) {
this.toRelease = toRelease;
}
public void Dispose()
{
toRelease.semaphore.Release();
}
}
} It would be really nice if an equivalent was included in .NET given how commonly this pattern is used. **EDIT: ** Changed |
@crozone While the semaphore does not need to be disposed when we don't call |
This is broken for the case that the cancellation token is cancelled. In the uncontented case, In both cases, this will lead to code being executed without the lock being held, and then when |
Further to my previous comment, I'd also add that it appears that many of the implementations of AsyncLock that are based on Stephen Toub's original (but with added support for cancellation) also have similar bugs. For example just by searching under the Microsoft organistation on Github: Orleans at least appears to get it correct (uses Although the Orleans implementation always allocates even on the successful case (presumably the motivation for having a separate All this seems to me to be evidence that it would be worth having a properly tested and supported primitive shipped with .NET. |
@antmjones What do you think about https://github.com/BalassaMarton/AsyncExtensions by @BalassaMarton? |
@cremor For a start, personally I'm not keen on supporting reentrancy over async/await, for the reasons described here: https://itnext.io/reentrant-recursive-async-lock-is-impossible-in-c-e9593f4aa38a (and arguably it's a bad idea even in the non-async case, see https://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html) Apart from that it's far more complex than the solutions based on Stephen Toub's original example (so more chance of bugs), and I personally wouldn't want to use any synchronization primitives described as "experimental"! |
@antmjones my repo is not published, nor is production-ready, but might be useful for someone who's trying to build an async locking library. The key idea is the locking token which you can pass down to subsequent methods that need to take the lock. |
Ahh of course. I think simply changing it to |
Using
Note that if using |
Just to add another vote towards building something of this kind into .NET, the prototype for AsyncRx.NET currently finds itself obliged to provide its own implementation of this type. Worse, that implementation needs to be public. (This is because classic Rx.NET's We would really much rather not be in the business of defining thread synchronization primitives. We've already had requests to enhance the capability of our The recurrence of this problem (and the fact that so many projects get it wrong when they bring their own implementation) seems like an argument in favour of building such a thing into the .NET runtime libraries. |
Would be nice if the behavior was unified on top of the recently introduced |
Please add @stephentoub's excellent AsyncLock and async primitive friends into Core FX.
I searched and was surprised not to find this suggested before. Feel free to close this if it was and I just couldn't find it.
While it is true that Stephen Cleary has added this to his great AsyncEx library, I think this is a common enough need that it should be part of the framework itself.
The text was updated successfully, but these errors were encountered: