C# 停止两个异步方法与另一个同步运行
我有两个异步函数,我将调用C# 停止两个异步方法与另一个同步运行,c#,.net,multithreading,concurrency,async-await,C#,.net,Multithreading,Concurrency,Async Await,我有两个异步函数,我将调用ChangeState()和DoThing()。它们都在等待下游异步方法。这些代码是从事件处理程序调用的,因此它们在执行时不会阻止任何其他代码。如果调用了ChangeState(),则必须在之前的任何ChangeState()完成之前,DoThing()不执行其操作更改状态()仍在执行时可以再次调用。在DoThing()之前开始的任何执行都应该在DoThing()可以继续之前完成 反之亦然ChangeState()应等待任何以前运行的DoStuff()完成 如何在没有死
ChangeState()
和DoThing()
。它们都在等待下游异步方法。这些代码是从事件处理程序调用的,因此它们在执行时不会阻止任何其他代码。如果调用了ChangeState()
,则必须在之前的任何ChangeState()
完成之前,DoThing()
不执行其操作<代码>更改状态()仍在执行时可以再次调用。在DoThing()
之前开始的任何执行都应该在DoThing()
可以继续之前完成
反之亦然ChangeState()
应等待任何以前运行的DoStuff()
完成
如何在没有死锁危险的情况下实现这一点
我知道在lock语句中不允许等待,这是有充分理由的,这就是我为什么不尝试重新创建该功能的原因
async void ChangeState(bool state)
{
//Wait here until any pending DoStuff() is complete.
await OutsideApi.ChangeState(state);
}
async void DoStuff()
{
//Wait here until any pending ChangeState() is complete.
await OutsideApi.DoStuff();
}
根据您的要求,像
ReaderWriterLock
这样的东西似乎可以帮助您。另外,由于您有async
方法,因此应该使用async
lock。不幸的是,.NET framework本身没有提供等待就绪的ReaderWriterLock
锁。幸运的是,你可以看看图书馆或这个。使用AsyncEx
的示例
var readerWriterLock = new AsyncReaderWriterLock();
async void ChangeState(bool state)
{
using(await readerWriterLock.ReaderLockAsync())
{
await OutsideApi.ChangeState(state);
}
}
async void DoStuff()
{
using(await readerWriterLock.WriterLockAsync())
{
await OutsideApi.DoStuff();
}
}
n.b.此解决方案仍然有一个限制,即
DoStuff
调用不能是并发的,writer lock,但仍然满足调用顺序以及在ChangeState
之前完成所有DoStuff
的要求,反之亦然。(来自@Scott Chamberlain使用读写器锁的提示)编辑:第一个解决方案不符合要求
此类跟踪从哪种类型(ChangeState和DoThing)运行的实例数量,并提供检查任务是否可以运行的方法
当任何ChangeState运行时,可以不等待地启动一个新线程,但当调用DoStuff时,它将等待直到所有ChangeState完成,这也是另一种工作方式。您可以使用ManualResetEvent或AutoResetEvent发出一个线程已完成的信号,以便另一个线程可以继续工作
可以找到一些示例:我为实践制作了一个名为
KeyedLock
的同步原语,该原语一次只允许对一个密钥执行并发异步操作。所有其他密钥排队,并在稍后分批(按密钥)取消阻止。该类的用途如下:
KeyedLock _keyedLock;
async Task ChangeState(bool state)
{
using (await this._keyedLock.LockAsync("ChangeState"))
{
await OutsideApi.ChangeState(state);
}
}
async Task DoStuff()
{
using (await this._keyedLock.LockAsync("DoStuff"))
{
await OutsideApi.DoStuff();
}
}
例如,下面的呼叫:
await ChangeState(true);
await DoStuff();
await DoStuff();
await ChangeState(false);
await DoStuff();
await ChangeState(true);
…将按以下顺序执行:
ChangeState(true);
ChangeState(false); // concurrently with the above
ChangeState(true); // concurrently with the above
DoStuff(); // after completion of the above
DoStuff(); // concurrently with the above
DoStuff(); // concurrently with the above
KeyedLock
类:
class KeyedLock
{
private object _currentKey;
private int _currentCount = 0;
private WaitingQueue _waitingQueue = new WaitingQueue();
private readonly object _locker = new object();
public Task WaitAsync(object key, CancellationToken cancellationToken)
{
if (key == null) throw new ArgumentNullException(nameof(key));
lock (_locker)
{
if (_currentKey != null && key != _currentKey)
{
var waiter = new TaskCompletionSource<bool>();
_waitingQueue.Enqueue(new KeyValuePair<object,
TaskCompletionSource<bool>>(key, waiter));
if (cancellationToken != null)
{
cancellationToken.Register(() => waiter.TrySetCanceled());
}
return waiter.Task;
}
else
{
_currentKey = key;
_currentCount++;
return cancellationToken.IsCancellationRequested ?
Task.FromCanceled(cancellationToken) : Task.FromResult(true);
}
}
}
public Task WaitAsync(object key) => WaitAsync(key, CancellationToken.None);
public void Release()
{
List<TaskCompletionSource<bool>> tasksToRelease;
lock (_locker)
{
if (_currentCount <= 0) throw new InvalidOperationException();
_currentCount--;
if (_currentCount > 0) return;
_currentKey = null;
if (_waitingQueue.Count == 0) return;
var newWaitingQueue = new WaitingQueue();
tasksToRelease = new List<TaskCompletionSource<bool>>();
foreach (var entry in _waitingQueue)
{
if (_currentKey == null || entry.Key == _currentKey)
{
_currentKey = entry.Key;
_currentCount++;
tasksToRelease.Add(entry.Value);
}
else
{
newWaitingQueue.Enqueue(entry);
}
}
_waitingQueue = newWaitingQueue;
}
foreach (var item in tasksToRelease)
{
item.TrySetResult(true);
}
}
private class WaitingQueue :
Queue<KeyValuePair<object, TaskCompletionSource<bool>>>
{ }
public Task<Releaser> LockAsync(object key,
CancellationToken cancellationToken)
{
var waitTask = this.WaitAsync(key, cancellationToken);
return waitTask.ContinueWith(
(_, state) => new Releaser((KeyedLock)state),
this, cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default
);
}
public Task<Releaser> LockAsync(object key)
=> LockAsync(key, CancellationToken.None);
public struct Releaser : IDisposable
{
private readonly KeyedLock _parent;
internal Releaser(KeyedLock parent) { _parent = parent; }
public void Dispose() { _parent?.Release(); }
}
}
类键锁
{
私有对象_currentKey;
私有整数_currentCount=0;
私有等待队列_WaitingQueue=新的等待队列();
私有只读对象_locker=新对象();
公共任务WaitAsync(对象密钥、CancellationToken CancellationToken)
{
如果(key==null)抛出新的ArgumentNullException(nameof(key));
锁(储物柜)
{
if(_currentKey!=null&&key!=\u currentKey)
{
var water=new TaskCompletionSource();
_排队(新的KeyValuePair(key,waiter));
if(cancellationToken!=null)
{
cancellationToken.Register(()=>waterer.TrySetCanceled());
}
返回服务员。任务;
}
其他的
{
_currentKey=key;
_currentCount++;
返回cancellationToken.IsCancellationRequested?
Task.fromCancelled(cancellationToken):Task.FromResult(true);
}
}
}
公共任务WaitAsync(对象键)=>WaitAsync(键,CancellationToken.None);
公开无效释放()
{
商店租赁清单;
锁(储物柜)
{
如果(_currentCount0)返回;
_currentKey=null;
如果(_waitingQueue.Count==0)返回;
var newWaitingQueue=newWaitingQueue();
tasksToRelease=新列表();
foreach(等待队列中的var条目)
{
if(_currentKey==null | | entry.Key==u currentKey)
{
_currentKey=entry.Key;
_currentCount++;
tasksToRelease.Add(entry.Value);
}
其他的
{
newWaitingQueue.Enqueue(条目);
}
}
_waitingQueue=新建waitingQueue;
}
foreach(tasksToRelease中的var项)
{
项目.TrySetResult(真);
}
}
私有类等待队列:
队列
{ }
公共任务锁定异步(对象密钥,
取消令牌(取消令牌)
{
var waitTask=this.WaitAsync(键,cancellationToken);
返回waitTask.ContinueWith(
(_,state)=>新释放器((KeyedLock)状态),
这个,取消令牌,
TaskContinuationOptions.Executes同步执行,
TaskScheduler.Default
);
}
公共任务锁定异步(对象密钥)
=>LockAsync(key,CancellationToken.None);
公共结构释放器:IDisposable
{
私有只读密钥锁\u父级;
内部释放器(KeyedLock parent){u parent=parent;}
public void Dispose(){u parent?.Release();}
}
}
这似乎非常适合一双ReaderWriterLockSlim
s
private readonly ReaderWriterLockSlim changeStateLock = new ReaderWriterLockSlim();
private readonly ReaderWriterLockSlim doStuffLock = new ReaderWriterLockSlim();
一个控制对ChangeState
的访问,另一个控制对DoStuff
的访问
读卡器锁用于表示一个方法正在执行,写卡器锁用于表示另一个方法正在执行。readerwriterlocksim
a
class KeyedLock
{
private object _currentKey;
private int _currentCount = 0;
private WaitingQueue _waitingQueue = new WaitingQueue();
private readonly object _locker = new object();
public Task WaitAsync(object key, CancellationToken cancellationToken)
{
if (key == null) throw new ArgumentNullException(nameof(key));
lock (_locker)
{
if (_currentKey != null && key != _currentKey)
{
var waiter = new TaskCompletionSource<bool>();
_waitingQueue.Enqueue(new KeyValuePair<object,
TaskCompletionSource<bool>>(key, waiter));
if (cancellationToken != null)
{
cancellationToken.Register(() => waiter.TrySetCanceled());
}
return waiter.Task;
}
else
{
_currentKey = key;
_currentCount++;
return cancellationToken.IsCancellationRequested ?
Task.FromCanceled(cancellationToken) : Task.FromResult(true);
}
}
}
public Task WaitAsync(object key) => WaitAsync(key, CancellationToken.None);
public void Release()
{
List<TaskCompletionSource<bool>> tasksToRelease;
lock (_locker)
{
if (_currentCount <= 0) throw new InvalidOperationException();
_currentCount--;
if (_currentCount > 0) return;
_currentKey = null;
if (_waitingQueue.Count == 0) return;
var newWaitingQueue = new WaitingQueue();
tasksToRelease = new List<TaskCompletionSource<bool>>();
foreach (var entry in _waitingQueue)
{
if (_currentKey == null || entry.Key == _currentKey)
{
_currentKey = entry.Key;
_currentCount++;
tasksToRelease.Add(entry.Value);
}
else
{
newWaitingQueue.Enqueue(entry);
}
}
_waitingQueue = newWaitingQueue;
}
foreach (var item in tasksToRelease)
{
item.TrySetResult(true);
}
}
private class WaitingQueue :
Queue<KeyValuePair<object, TaskCompletionSource<bool>>>
{ }
public Task<Releaser> LockAsync(object key,
CancellationToken cancellationToken)
{
var waitTask = this.WaitAsync(key, cancellationToken);
return waitTask.ContinueWith(
(_, state) => new Releaser((KeyedLock)state),
this, cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default
);
}
public Task<Releaser> LockAsync(object key)
=> LockAsync(key, CancellationToken.None);
public struct Releaser : IDisposable
{
private readonly KeyedLock _parent;
internal Releaser(KeyedLock parent) { _parent = parent; }
public void Dispose() { _parent?.Release(); }
}
}
private readonly ReaderWriterLockSlim changeStateLock = new ReaderWriterLockSlim();
private readonly ReaderWriterLockSlim doStuffLock = new ReaderWriterLockSlim();
async Task ChangeState(bool state)
{
await Task.Yield();
doStuffLock.EnterWriteLock();
try
{
changeStateLock.EnterReadLock();
try
{
await OutsideApi.ChangeState(state);
}
finally
{
changeStateLock.ExitReadLock();
}
}
finally
{
doStuffLock.ExitWriteLock();
}
}
async Task DoStuff()
{
await Task.Yield();
changeStateLock.EnterWriteLock();
try
{
doStuffLock.EnterReadLock();
try
{
await OutsideApi.DoStuff();
}
finally
{
doStuffLock.ExitReadLock();
}
}
finally
{
changeStateLock.ExitWriteLock();
}
}