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();
        }
    }