加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 取消SemaphoreSlim.Wait同步保持信号锁

发布时间:2020-12-15 06:27:06 所属栏目:百科 来源:网络整理
导读:在我们的一个课程中,我们大量使用 SemaphoreSlim.WaitAsync(CancellationToken) 并取消它. 在调用 SemaphoreSlim.Release() (不久之后,我的意思是在ThreadPool有机会处理排队的项目之前)等待等待WaitAsync的调用时,我似乎遇到了一个问题,它使信号量处于不存
在我们的一个课程中,我们大量使用 SemaphoreSlim.WaitAsync(CancellationToken)并取消它.

在调用SemaphoreSlim.Release()(不久之后,我的意思是在ThreadPool有机会处理排队的项目之前)等待等待WaitAsync的调用时,我似乎遇到了一个问题,它使信号量处于不存在的状态可以获取进一步的锁.

由于ThreadPool项是否在调用Release()和Cancel()之间执行的非确定性性质,以下示例并不总是表明问题,因为这些情况,我已明确表示忽略该运行.

这是我试图演示问题的例子:

void Main()
{
    for(var i = 0; i < 100000; ++i)
        Task.Run(new Func<Task>(SemaphoreSlimWaitAsyncCancellationBug)).Wait();
}

private static async Task SemaphoreSlimWaitAsyncCancellationBug()
{
    // Only allow one thread at a time
    using (var semaphore = new SemaphoreSlim(1,1))
    {
        // Block any waits
        semaphore.Wait();

        using(var cts1 = new CancellationTokenSource())
        {
            var wait2 = semaphore.WaitAsync(cts1.Token);
            Debug.Assert(!wait2.IsCompleted,"Should be blocked by the existing wait");

            // Release the existing wait
            // After this point,wait2 may get completed or it may not (depending upon the execution of a ThreadPool item)
            semaphore.Release();         

            // If wait2 was not completed,it should now be cancelled
            cts1.Cancel();             

            if(wait2.Status == TaskStatus.RanToCompletion)
            {
                // Ignore this run; the lock was acquired before cancellation
                return;
            }

            var wasCanceled = false;
            try
            {
                await wait2.ConfigureAwait(false);

                // Ignore this run; this should only be hit if the wait lock was acquired
                return;
            }
            catch(OperationCanceledException)
            {
                wasCanceled = true;
            }

            Debug.Assert(wasCanceled,"Should have been canceled");            
            Debug.Assert(semaphore.CurrentCount > 0,"The first wait was released,and the second was canceled so why can no threads enter?");
        }
    }
}

而here是LINQPad实现的一个链接.

运行上一个示例几次,有时您会看到WaitAsync的取消不再允许任何线程进入.

更新

看来这是不可重现的每台机器,如果你设法重现的问题,请留下这样的评论.

我已经设法重现了以下问题:

>运行i7-2600的3x 64位Windows 7机器
> 64位Windows 8机器运行i7-3630QM

我以前无法重现这个问题:

> 64位Windows 8机器运行i5-2500k

更新2

我已经提交了Microsoft here的错误,但到目前为止,他们无法重现,所以如果尽可能多的尝试和运行示例项目,它可能会有帮助,它可以在链接问题的附件选项卡上找到.

解决方法

.NET 4.5.1中更改了SemaphoreSlim

.NET 4.5 WaitUntilCountOrTimeoutAsync方法的版本是:

private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter,int millisecondsTimeout,CancellationToken cancellationToken)
{ 
    [...]

    // If the await completed synchronously,we still hold the lock.  If it didn't,// we no longer hold the lock.  As such,acquire it. 
    lock (m_lockObj)
    { 
        RemoveAsyncWaiter(asyncWaiter);
        if (asyncWaiter.IsCompleted)
        {
            Contract.Assert(asyncWaiter.Status == TaskStatus.RanToCompletion && asyncWaiter.Result,"Expected waiter to complete successfully");
            return true; // successfully acquired 
        } 
        cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
        return false; // timeout occurred 
    }
}

4.5.1中的相同方法:

private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter,CancellationToken cancellationToken)
{
    [...]

    lock (m_lockObj)
    {
        if (RemoveAsyncWaiter(asyncWaiter))
        {
            cancellationToken.ThrowIfCancellationRequested(); 
            return false; 
        }
    }

    return await asyncWaiter.ConfigureAwait(false);
}

asyncWaiter基本上是一个总是返回true的任务(在单独的线程中完成,总是使用True结果).

释放方法调用RemoveAsyncWaiter并调度worker以完成true.

这是4.5中的一个可能的问题:

RemoveAsyncWaiter(asyncWaiter);
    if (asyncWaiter.IsCompleted)
    {
        Contract.Assert(asyncWaiter.Status == TaskStatus.RanToCompletion && asyncWaiter.Result,"Expected waiter to complete successfully");
        return true; // successfully acquired 
    } 
    //! another thread calls Release
    //! asyncWaiter completes with true,Wait should return true
    //! CurrentCount will be 0

    cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred,//! throws OperationCanceledException
    //! wasCanceled will be true

    return false; // timeout occurred

在4.5.1 RemoveAsyncWaiter将返回false,WaitAsync将返回true.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读