2014-06-05 43 views
2

我遇到了一個問題,我需要限制調用另一個Web服務器的次數。它會有所不同,因爲服務器是共享的,也許它可能有更多或更少的容量。帶動態信號量限制maxCount

我正在考慮使用SemaphoreSlim類,但沒有公共屬性來更改最大數量。

我是否應該將SemaphoreSlim類包裝到另一個可處理最大數量的類中?有沒有更好的方法?

編輯:

這裏的我想要什麼:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Semaphore 
{ 
class Program 
{ 
    static SemaphoreSlim _sem = new SemaphoreSlim(10,10000); 

    static void Main(string[] args) 
    { 
     int max = 15; 

     for (int i = 1; i <= 50; i++) 
     { 
      new Thread(Enter).Start(new int[] { i, max}); 
     } 

     Console.ReadLine(); 

     max = 11; 

     for (int i = 1; i <= 50; i++) 
     { 
      new Thread(Enter).Start(new int[] { i, max }); 
     } 
    } 

    static void Enter(object param) 
    { 
     int[] arr = (int[])param; 
     int id = arr[0]; 
     int max = arr[1]; 

     try 
     { 
      Console.WriteLine(_sem.CurrentCount); 

      if (_sem.CurrentCount <= max) 
       _sem.Release(1); 
      else 
      { 
       _sem.Wait(1000); 

       Console.WriteLine(id + " wants to enter"); 

       Thread.Sleep((1000 * id)/2); // can be here at 

       Console.WriteLine(id + " is in!"); // Only three threads 

      } 
     } 
     catch(Exception ex) 
     { 
      Console.WriteLine("opps ", id); 
      Console.WriteLine(ex.Message); 
     } 
     finally    
     { 
      _sem.Release(); 
     } 
    } 
} 
} 

問題:

1 _sem.Wait(1000)應取消線程的執行將超過執行1000毫秒,不是嗎?

2 - 我有使用Release/Wait的想法嗎?

回答

2
  1. 得到一個信號量。
  2. 將容量設置得比您需要的要高很多。
  3. 將初始容量設置爲您想要的最大容量爲實際
  4. 把信號給他人使用。

在這一點上,你可以等待信號量,無論你想要多少(沒有相應的釋放調用)來降低容量。您可以多次釋放信號量(沒有相應的等待呼叫)以增加有效容量。

如果這是你做得夠多的事情,你可以創建自己的信號量類,它構成了一個SemaphoreSlim並封裝了這個邏輯。如果你的代碼已經在沒有先等待的情況下釋放一個信號量,那麼這個組合也是很重要的。與你自己的班級,你可以確保這樣的發佈是沒有操作。 (這就是說,你應該避免把自己在那個位置上開始的,真的。)

+0

或者只是調用構造函數:http://msdn.microsoft.com/en-us/library/dd270891(v=vs.110).aspx –

+0

即使我自己的類,它封裝SemaphoreSlim,我需要靈活地將最大併發呼叫切換爲上或下。即從1000開始,改爲600,並在一段時間後改爲1700. –

+0

@JimMischel當然,儘管如果你想真的能夠改變適當的最大值,你真的需要用另一種類型來組合它,這樣你才能確保當它已經達到最大值時釋放它,而不增加最大值就成爲一個noop(或一個例外)。 – Servy

5

你不能改變的最大計數,但你可以創建一個SemaphoreSlim具有非常高的最大計數,並保留一些他們。見this constructor

所以我們可以說的是,絕對最大數量的併發呼叫100,但一開始你希望它是25.你初始化你的信號:

SemaphoreSlim sem = new SemaphoreSlim(25, 100); 

所以25是請求的數量,可以同時服務。您已預訂其他75.

如果您想要增加允許的數量,請致電Release(num)。如果您撥打Release(10),那麼電話號碼將轉爲35.

現在,如果您想減少可用請求的數量,則必須多次撥打WaitOne。例如,如果你想從可用計數刪除10:

for (var i = 0; i < 10; ++i) 
{ 
    sem.WaitOne(); 
} 

這有阻塞,直到其他客戶端釋放信號量的潛力。也就是說,如果允許35個併發請求,並且希望將其減少到25個,但已有35個客戶端進行活動請求,那麼WaitOne將阻塞,直到客戶端調用Release,並且直到10個客戶端發佈時,循環纔會終止。

+0

這可能有幫助,但我需要靈活的東西。可以說,最多1000個併發,但幾個小時後,最大值應該是600或1200.我相信SemaphoreSlim不會給我這種靈活性。 =( –

+3

@ThiagoCustodio:你甚至讀過答案嗎?將第二個參數設置爲你將允許的*最大值*。然後你可以使用'Release'和'WaitOne'來調整可用的數字 –

+0

I我會嘗試一下,非常感謝 –

1

好吧,我可以解決我的問題看單聲道項目。

// SemaphoreSlim.cs 
// 
// Copyright (c) 2008 Jérémie "Garuma" Laval 
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions: 
// 
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software. 
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
// THE SOFTWARE. 
// 
// 

using System; 
using System.Diagnostics; 
using System.Threading.Tasks; 

namespace System.Threading 
{ 
    public class SemaphoreSlimCustom : IDisposable 
    { 
     const int spinCount = 10; 
     const int deepSleepTime = 20; 
     private object _sync = new object(); 


     int maxCount; 
     int currCount; 
     bool isDisposed; 

     public int MaxCount 
     { 
      get { lock (_sync) { return maxCount; } } 
      set 
      { 
       lock (_sync) 
       { 
        maxCount = value; 
       } 
      } 
     } 

     EventWaitHandle handle; 

     public SemaphoreSlimCustom (int initialCount) : this (initialCount, int.MaxValue) 
     { 
     } 

     public SemaphoreSlimCustom (int initialCount, int maxCount) 
     { 
      if (initialCount < 0 || initialCount > maxCount || maxCount < 0) 
       throw new ArgumentOutOfRangeException ("The initialCount argument is negative, initialCount is greater than maxCount, or maxCount is not positive."); 

      this.maxCount = maxCount; 
      this.currCount = initialCount; 
      this.handle = new ManualResetEvent (initialCount > 0); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
     } 

     protected virtual void Dispose (bool disposing) 
     { 
      isDisposed = true; 
     } 

     void CheckState() 
     { 
      if (isDisposed) 
       throw new ObjectDisposedException ("The SemaphoreSlim has been disposed."); 
     } 

     public int CurrentCount { 
      get { 
       return currCount; 
      } 
     } 

     public int Release() 
     { 
      return Release(1); 
     } 

     public int Release (int releaseCount) 
     { 
      CheckState(); 
      if (releaseCount < 1) 
       throw new ArgumentOutOfRangeException ("releaseCount", "releaseCount is less than 1"); 

      // As we have to take care of the max limit we resort to CAS 
      int oldValue, newValue; 
      do { 
       oldValue = currCount; 
       newValue = (currCount + releaseCount); 
       newValue = newValue > maxCount ? maxCount : newValue; 
      } while (Interlocked.CompareExchange (ref currCount, newValue, oldValue) != oldValue); 

      handle.Set(); 

      return oldValue; 
     } 

     public void Wait() 
     { 
      Wait (CancellationToken.None); 
     } 

     public bool Wait (TimeSpan timeout) 
     { 
      return Wait ((int)timeout.TotalMilliseconds, CancellationToken.None); 
     } 

     public bool Wait (int millisecondsTimeout) 
     { 
      return Wait (millisecondsTimeout, CancellationToken.None); 
     } 

     public void Wait (CancellationToken cancellationToken) 
     { 
      Wait (-1, cancellationToken); 
     } 

     public bool Wait (TimeSpan timeout, CancellationToken cancellationToken) 
     { 
      CheckState(); 
      return Wait ((int)timeout.TotalMilliseconds, cancellationToken); 
     } 

     public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken) 
     { 
      CheckState(); 
      if (millisecondsTimeout < -1) 
       throw new ArgumentOutOfRangeException ("millisecondsTimeout", 
                 "millisecondsTimeout is a negative number other than -1"); 

      Stopwatch sw = Stopwatch.StartNew(); 

      Func<bool> stopCondition =() => millisecondsTimeout >= 0 && sw.ElapsedMilliseconds > millisecondsTimeout; 

      do { 
       bool shouldWait; 
       int result; 

       do { 
        cancellationToken.ThrowIfCancellationRequested(); 
        if (stopCondition()) 
         return false; 

        shouldWait = true; 
        result = currCount; 

        if (result > 0) 
         shouldWait = false; 
        else 
         break; 
       } while (Interlocked.CompareExchange (ref currCount, result - 1, result) != result); 

       if (!shouldWait) { 
        if (result == 1) 
         handle.Reset(); 
        break; 
       } 

       SpinWait wait = new SpinWait(); 

       while (Thread.VolatileRead (ref currCount) <= 0) { 
        cancellationToken.ThrowIfCancellationRequested(); 
        if (stopCondition()) 
         return false; 

        if (wait.Count > spinCount) { 
         int diff = millisecondsTimeout - (int)sw.ElapsedMilliseconds; 

         int timeout = millisecondsTimeout < 0 ? deepSleepTime : 


          Math.Min (Math.Max (diff, 1), deepSleepTime); 
         handle.WaitOne (timeout); 
        } else 
         wait.SpinOnce(); 
       } 
      } while (true); 

      return true; 
     } 

     public WaitHandle AvailableWaitHandle { 
      get { 
       return handle; 
      } 
     } 

     public Task WaitAsync() 
     { 
      return Task.Factory.StartNew (() => Wait()); 
     } 

     public Task WaitAsync (CancellationToken cancellationToken) 
     { 
      return Task.Factory.StartNew (() => Wait (cancellationToken), cancellationToken); 
     } 

     public Task<bool> WaitAsync (int millisecondsTimeout) 
     { 
      return Task.Factory.StartNew (() => Wait (millisecondsTimeout)); 
     } 

     public Task<bool> WaitAsync (TimeSpan timeout) 
     { 
      return Task.Factory.StartNew (() => Wait (timeout)); 
     } 

     public Task<bool> WaitAsync (int millisecondsTimeout, CancellationToken cancellationToken) 
     { 
      return Task.Factory.StartNew (() => Wait (millisecondsTimeout, cancellationToken), cancellationToken); 
     } 

     public Task<bool> WaitAsync (TimeSpan timeout, CancellationToken cancellationToken) 
     { 
      return Task.Factory.StartNew (() => Wait (timeout, cancellationToken), cancellationToken); 
     } 
    } 
}