具有beginwait功能的信号量

时间:2013-09-03 15:59:36

标签: c# asynchronous semaphore

我正在使用begin / end编写一个异步库,我需要锁定对象。

目前,我正在使用信号量进行此操作,但调用semaphore.WaitOne()会挂起调用它的线程。我宁愿使用类似BeginWait的东西,所以当信号量空闲时它会立即返回并调用回调函数。

c#中有这样的对象吗?

2 个答案:

答案 0 :(得分:2)

您可以使用WaitAsync(.NET 4.5+)的SemaphoreSlim方法获取将在信号量下次可用时完成的Task。您可以向该任务添加一个延续,以便在信号量处于活动状态时调用该回调。

答案 1 :(得分:0)

实际上,这就是我最终得到的结果 - 也许有人会发现它很有用:

class AsyncSemaphore
{
    public AsyncSemaphore(int counter, int maxCounter)
    {
        this.maxCounter = maxCounter;
        this.counter = counter;
    }

    private int maxCounter, counter;

    class CallbackObject:IAsyncResult
    {
        private AsyncCallback callback;

        #region IAsyncResult Members

        public object AsyncState { get; set; }

        public System.Threading.WaitHandle AsyncWaitHandle
        {
            get 
            {
                throw new NotImplementedException();
            }
        }

        public bool CompletedSynchronously { get; set; }

        public bool IsCompleted { get; set; }

        public AsyncCallback Callback
        {
            get { return callback; }
            set { callback = value; }
        }

        #endregion

    }

    private ConcurrentQueue<CallbackObject> queue = new ConcurrentQueue<CallbackObject>();

    public IAsyncResult BeginWait(AsyncCallback callback, object state)
    {
        if (callback==null)
        {
            throw new ArgumentNullException("callback","callback cannot be null");
        }
        var o=new CallbackObject();
        o.AsyncState = state;
        o.Callback = callback;
        bool execute = false;

        if (Interlocked.Decrement(ref this.counter)>=0)
        {
            o.CompletedSynchronously= execute = true;
        }
        else
        {
            queue.Enqueue(o);
        }

        if (execute)
        {
            callback(o);
            o.IsCompleted = true;
        }

        return o;
    }

    public void EndWait(IAsyncResult r)
    {}

    public void Release()
    {
        CallbackObject execute = null;

        if (Interlocked.Increment(ref this.counter)<1)
        {
            if (!queue.TryDequeue(out execute))
            {
                throw new NotImplementedException("ConcurrentQueue.TryDequeue failed");
            }
        }
        else
        {
            if (counter > maxCounter)
            {
                throw new SemaphoreFullException("Release was called too many times");
            } 
        }

        if (execute!=null)
        {
            execute.Callback(execute);
            execute.IsCompleted = true;
        }
    }
}