C# 可取消信号量LIM-您是否建议对我的封装进行任何改进?

C# 可取消信号量LIM-您是否建议对我的封装进行任何改进?,c#,semaphore,cancellation,C#,Semaphore,Cancellation,出于X的原因,我需要编写一个信号量封装,它允许我取消我的信号量lim上所有等待的进程。(信号量LIM取消封装) 这是我的班级: public class CancellableSemaphoreSlim { readonly Queue<CancellationTokenSource> tokens = new Queue<CancellationTokenSource>(); readonly SemaphoreSlim ss; /// <

出于X的原因,我需要编写一个信号量封装,它允许我取消我的
信号量lim
上所有等待的进程。(信号量LIM取消封装)

这是我的班级:

public class CancellableSemaphoreSlim
{
    readonly Queue<CancellationTokenSource> tokens = new Queue<CancellationTokenSource>();
    readonly SemaphoreSlim ss;

    /// <summary>
    /// Initializes a new instance of the <see cref="T:Eyes.Mobile.Core.Helpers.CancellableSemaphoreSlim"/> class.
    /// </summary>
    /// <param name="initialCount">Initial count.</param>
    public CancellableSemaphoreSlim(int initialCount) { ss = new SemaphoreSlim(initialCount); }

    /// <summary>Asynchronously waits to enter the <see cref="T:System.Threading.SemaphoreSlim" />, while observing a <see cref="T:System.Threading.CancellationToken" />. </summary>
    /// <returns>A task that will complete when the semaphore has been entered. </returns>
    /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
    /// <exception cref="T:System.OperationCanceledException" />
    public Task WaitAsync()
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        tokens.Enqueue(cancellationTokenSource);
        return ss.WaitAsync(cancellationTokenSource.Token);
    }

    /// <summary>Asynchronously waits to enter the <see cref="T:System.Threading.SemaphoreSlim" />, while observing a <see cref="T:System.Threading.CancellationTokenSource" />. </summary>
    /// <returns>A task that will complete when the semaphore has been entered. </returns>
    /// <param name="cancellationTokenSource">The <see cref="T:System.Threading.CancellationToken" /> token to observe.</param>
    /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
    /// <exception cref="T:System.OperationCanceledException">
    ///     <paramref name="cancellationTokenSource" /> was canceled. 
    /// </exception>
    public Task WaitAsync(CancellationToken cancellationToken)
    {
        CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        tokens.Enqueue(cancellationTokenSource);
        return ss.WaitAsync(cancellationTokenSource.Token);
    }

    /// <summary>
    /// Release this instance.
    /// </summary>
    /// <returns>The released semaphore return.</returns>
    public int Release() => ss.Release();

    /// <summary>
    /// Cancel all processus currently in WaitAsync() state.
    /// </summary>
    public void CancelAll()
    {
        while (tokens.Count > 0)
        {
            CancellationTokenSource token = tokens.Dequeue();
            if (!token.IsCancellationRequested)
                token.Cancel();
        }
    }
}
但是,我想知道使用
队列是否会产生任何异步问题?因为,如果我们有一个可以由不同线程/任务调用的方法(MakeAAction-like),如果在新的任务/线程调用MakeAAction之前调用了CancelAll(),这意味着这个方法将被添加到队列中,而该队列实际上正在使其所有项目退出队列

因此,我考虑尝试使用
CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)
在我所有的取消令牌之间创建一个唯一的链接。然而,即使它是一个varargs逻辑(
params
),它会产生同样的问题吗

我只是想以一种不会失败的方式来实现它,但我想我目前的方法不太好,所以我想知道是否有人能为我提供一个关于这种封装及其逻辑的观点

如果你认为有些东西不合逻辑,请随时给我提建议:)

马克斯

编辑1 然后我编辑了代码,以便跟随@NthDeveloper的讨论。我试图添加系统

public class CancellableSemaphoreSlim
{
    object _syncObj = new object();
    readonly Queue<CancellationTokenSource> tokens = new Queue<CancellationTokenSource>();
    readonly SemaphoreSlim ss;

    /// <summary>
    /// Initializes a new instance of the <see cref="T:Eyes.Mobile.Core.Helpers.CancellableSemaphoreSlim"/> class.
    /// </summary>
    /// <param name="initialCount">Initial count.</param>
    public CancellableSemaphoreSlim(int initialCount) { ss = new SemaphoreSlim(initialCount); }

    /// <summary>Asynchronously waits to enter the <see cref="T:System.Threading.SemaphoreSlim" />, while observing a <see cref="T:System.Threading.CancellationToken" />. </summary>
    /// <returns>A task that will complete when the semaphore has been entered. </returns>
    /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
    /// <exception cref="T:System.OperationCanceledException" />
    public Task WaitAsync()
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        lock (_syncObj)
        {
            tokens.Enqueue(cancellationTokenSource);
        }
        return ss.WaitAsync(cancellationTokenSource.Token);
    }

    /// <summary>Asynchronously waits to enter the <see cref="T:System.Threading.SemaphoreSlim" />, while observing a <see cref="T:System.Threading.CancellationTokenSource" />. </summary>
    /// <returns>A task that will complete when the semaphore has been entered. </returns>
    /// <param name="cancellationTokenSource">The <see cref="T:System.Threading.CancellationToken" /> token to observe.</param>
    /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception>
    /// <exception cref="T:System.OperationCanceledException">
    ///     <paramref name="cancellationTokenSource" /> was canceled. 
    /// </exception>
    public Task WaitAsync(CancellationToken cancellationToken)
    {
        CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        lock (_syncObj)
        {
            tokens.Enqueue(cancellationTokenSource);
        }
        return ss.WaitAsync(cancellationTokenSource.Token);
    }

    /// <summary>
    /// Release this instance.
    /// </summary>
    /// <returns>The released semaphore return.</returns>
    public int Release() => ss.Release();

    /// <summary>
    /// Cancel all processus currently in WaitAsync() state.
    /// </summary>
    public void CancelAll()
    {
        lock (_syncObj)
        {
            while (tokens.Count > 0)
            {
                CancellationTokenSource token = tokens.Dequeue();
                if (!token.IsCancellationRequested)
                    token.Cancel();
            }
        }
    }
}
public类cancelablesemaphoreslim
{
对象_syncObj=新对象();
只读队列令牌=新队列();
只读信号量lim ss;
/// 
///初始化类的新实例。
/// 
///初始计数。
公共可取消信号量lim(int initialCount){ss=新信号量lim(initialCount);}
///异步等待输入,同时观察。
///输入信号量后将完成的任务。
///当前实例已被释放。
/// 
公共任务WaitAsync()
{
CancellationTokenSource CancellationTokenSource=新的CancellationTokenSource();
锁定(_syncObj)
{
令牌。排队(cancellationTokenSource);
}
返回ss.WaitAsync(cancellationTokenSource.Token);
}
///异步等待输入,同时观察。
///输入信号量后将完成的任务。
///要观察的标记。
///当前实例已被释放。
/// 
///取消了。
/// 
公共任务WaitAsync(CancellationToken CancellationToken)
{
CancellationTokenSource CancellationTokenSource=CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
锁定(_syncObj)
{
令牌。排队(cancellationTokenSource);
}
返回ss.WaitAsync(cancellationTokenSource.Token);
}
/// 
///释放此实例。
/// 
///释放的信号量返回。
public int Release()=>ss.Release();
/// 
///取消当前处于WaitAsync()状态的所有processus。
/// 
公共作废取消全部()
{
锁定(_syncObj)
{
而(tokens.Count>0)
{
CancellationTokenSource token=tokens.Dequeue();
如果(!token.IsCancellationRequested)
token.Cancel();
}
}
}
}

示例线程安全类,用于保护其内部列表不受同时更改的影响,并防止在释放该类后使用该类

public class SampleThreadSafeDisposableClass: IDisposable
{
    bool _isDisposed;
    object _syncObj = new object();

    List<object> _list = new List<object>();

    public void Add(object obj)
    {
        lock(_syncObj)
        {
            if (_isDisposed)
                return;

            _list.Add(obj);
        }
    }       

    //This method can be Dispose/Clear/CancelAll
    public void Dispose()
    {
        lock (_syncObj)
        {
            if (_isDisposed)
                return;

            _isDisposed = true;

            _list.Clear();
        }
    }
}
public类SampleThreadSafeDisposableClass:IDisposable
{
布卢被发现;
对象_syncObj=新对象();
列表_List=新列表();
公共无效添加(对象obj)
{
锁定(_syncObj)
{
如果(_isDisposed)
返回;
_列表。添加(obj);
}
}       
//此方法可以是Dispose/Clear/CancelAll
公共空间处置()
{
锁定(_syncObj)
{
如果(_isDisposed)
返回;
_isDisposed=true;
_list.Clear();
}
}
}

希望这能有所帮助。

示例线程安全类,该类保护其内部列表不受同时更改的影响,并防止该类在被释放后被使用

public class SampleThreadSafeDisposableClass: IDisposable
{
    bool _isDisposed;
    object _syncObj = new object();

    List<object> _list = new List<object>();

    public void Add(object obj)
    {
        lock(_syncObj)
        {
            if (_isDisposed)
                return;

            _list.Add(obj);
        }
    }       

    //This method can be Dispose/Clear/CancelAll
    public void Dispose()
    {
        lock (_syncObj)
        {
            if (_isDisposed)
                return;

            _isDisposed = true;

            _list.Clear();
        }
    }
}
public类SampleThreadSafeDisposableClass:IDisposable
{
布卢被发现;
对象_syncObj=新对象();
列表_List=新列表();
公共无效添加(对象obj)
{
锁定(_syncObj)
{
如果(_isDisposed)
返回;
_列表。添加(obj);
}
}       
//此方法可以是Dispose/Clear/CancelAll
公共空间处置()
{
锁定(_syncObj)
{
如果(_isDisposed)
返回;
_isDisposed=true;
_list.Clear();
}
}
}

希望这能有所帮助。

我认为您可以通过只使用一个
CancellationSource
来简化代码,它会在
CancelAll
中触发并与新代码交换:

public class CancellableSemaphoreSlim
{
    CancellationTokenSource cancelSource = new CancellationTokenSource();
    readonly SemaphoreSlim ss;

    public CancellableSemaphoreSlim(int initialCount) 
    { 
        ss = new SemaphoreSlim(initialCount); 
    }

    public Task WaitAsync() => ss.WaitAsync(cancelSource.Token);

    public Task WaitAsync(CancellationToken cancellationToken)
    {
        // This operation will cancel when either the user token or our cancelSource signal cancellation
        CancellationTokenSource linkedSource =  CancellationTokenSource.CreateLinkedTokenSource(cancelSource.Token, cancellationToken);
        return ss.WaitAsync(linkedSource.Token);
    }

    public int Release() => ss.Release();

    public void CancelAll()
    {
        var currentCancelSource = Interlocked.Exchange(ref cancelSource, new CancellationTokenSource());
        currentCancelSource.Cancel();
    }
}
总是会有各种各样的竞争来确定
WaitAsync
是否被同时运行的
CancelAll
调用取消


在这个版本中,取决于是旧的还是新的
cancelSource.Token
WaitAsync()

中被抓取,我认为您可以通过只使用一个
CancelationSource
来简化代码,该代码在
CancelAll
中被触发并与新代码交换:

public class CancellableSemaphoreSlim
{
    CancellationTokenSource cancelSource = new CancellationTokenSource();
    readonly SemaphoreSlim ss;

    public CancellableSemaphoreSlim(int initialCount) 
    { 
        ss = new SemaphoreSlim(initialCount); 
    }

    public Task WaitAsync() => ss.WaitAsync(cancelSource.Token);

    public Task WaitAsync(CancellationToken cancellationToken)
    {
        // This operation will cancel when either the user token or our cancelSource signal cancellation
        CancellationTokenSource linkedSource =  CancellationTokenSource.CreateLinkedTokenSource(cancelSource.Token, cancellationToken);
        return ss.WaitAsync(linkedSource.Token);
    }

    public int Release() => ss.Release();

    public void CancelAll()
    {
        var currentCancelSource = Interlocked.Exchange(ref cancelSource, new CancellationTokenSource());
        currentCancelSource.Cancel();
    }
}
总是会有各种各样的竞争来确定
WaitAsync
是否被同时运行的
CancelAll
调用取消


在这个版本中,取决于是在
WaitAsync()

中抓取旧的还是新的
cancelSource.Token
如果我没有错,您希望保护您的类不受来自不同线程的调用。我是说