【问题标题】:A semaphore with beginwait function带有 beginwait 功能的信号量
【发布时间】:2013-09-03 15:59:36
【问题描述】:

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

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

c#中有这样的对象吗?

【问题讨论】:

  • blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266983.aspx 怎么样?不是开始/回调,而是基于任务。
  • 你问的没有任何意义。线程不能在等待信号量的同时做其他事情。除非您定期轮询信号量。给我们更多关于你想要做什么的信息。我想你问错问题了。
  • @JimMischel 异步模型的想法是根本不让线程进行阻塞等待,而不是创建新线程只是为了让它们为您进行阻塞等待。

标签: c# asynchronous semaphore


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    实际上,这就是我最终得到的——也许有人会觉得它有用:

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

    【讨论】:

    • 您可以使用ConcurrentQueue 来避免需要锁定队列,并且可以使用Interlocked 方法来操作counter 以避免需要围绕它进行锁定。在这两者之间,您实际上可以迁移到完全无锁的模型。
    • 谢谢!我没有使用并发队列,因为我认为它在内部使用了锁。太棒了!
    • 另外,我不确定如何在不使用 Interlocked 创建竞争条件的情况下正确实现此代码:if (counter&gt;0) { o.CompletedSynchronously= execute = true; counter--; }
    猜你喜欢
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多