2011-05-01 133 views
2

我在這裏要求任何ConcurentSet/ConcurentQueue執行how to create no-duplicates ConcurrentQueue?,但沒有人提出任何建議。爲什麼我的ConcurentSet不起作用?

所以我決定自己寫了,稍微修改了MSDN例子。 我創建了ConcurentSet類。我插入相同的元素兩次,並期望它只能插入一次,因爲Set用於內部:

   numbers.Add(i); 
       Console.WriteLine("Add:{0} Count={1}", i, numbers.Count); 
       numbers.Add(i); 
       Console.WriteLine("Add:{0} Count={1}", i, numbers.Count); 

然而,根據兩次插入輸出要素:

Add:0 Count=0 
    Add:0 Count=1 
    Add:1 Count=2 
    Add:1 Count=3 
    Add:2 Count=4 
    Take:0 
    Add:2 Count=5 
    Add:3 Count=6 
    Add:3 Count=7 
    Add:4 Count=7 
    Add:4 Count=8 
    Take:3 
    Add:5 Count=9 
    Add:5 Count=10 
    Add:6 Count=11 
    Add:6 Count=12 

的問題是 - 爲什麼我的ConcurentSet實現不按預期工作? 我希望這樣的輸出:

Add:0 Count=0 
    Add:0 Count=0 
    Add:1 Count=1 
    Add:1 Count=1 
    Add:2 Count=2 
    Take:0 
    Add:2 Count=1 
    Add:3 Count=2 
    Add:3 Count=2 
    Add:4 Count=3 
    Add:4 Count=3 
    Take:3 
    Add:5 Count=3 
    Add:5 Count=3 
    ..... 

完整清單如下:

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

namespace BCBlockingAccess 
{ 
using System; 
using System.Collections.Concurrent; 

public class ConcurentSet 
: IProducerConsumerCollection<int> 
{ 
    private readonly object m_lockObject = new object(); 
    private readonly HashSet<int> m_set = new HashSet<int>(); 

    public void CopyTo(Array array, int index) 
    { 
     throw new NotImplementedException(); 
    } 

    public int Count { get { return m_set.Count; } } 

    public object SyncRoot { get { return m_lockObject; } } 

    public bool IsSynchronized { get { return true; } } 

    public void CopyTo(int[] array, int index) 
    { 
     throw new NotImplementedException(); 
    } 

    public bool TryAdd(int item) 
    { 
     lock (m_lockObject) 
     { 
      m_set.Add(item); 
     } 

     return true; 
    } 

    public bool TryTake(out int item) 
    { 
     lock (m_lockObject) 
     { 
      foreach (var i in m_set) 
      { 
       if (m_set.Remove(i)) 
       { 
        item = i; 
        return true; 
       } 
      } 
      item = -1; 
      return false; 
     } 
    } 

    public int[] ToArray() 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerator<int> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     // Increase or decrease this value as desired. 
     int itemsToAdd = 50; 

     // Preserve all the display output for Adds and Takes 
     Console.SetBufferSize(80, (itemsToAdd * 5) + 3); 

     // A bounded collection. Increase, decrease, or remove the 
     // maximum capacity argument to see how it impacts behavior. 
     BlockingCollection<int> numbers = new BlockingCollection<int>(new ConcurentSet()); 


     // A simple blocking consumer with no cancellation. 
     Task.Factory.StartNew(() => 
     { 
      int i = -1; 
      while (!numbers.IsCompleted) 
      { 
       try 
       { 
        i = numbers.Take(); 
       } 
       catch (InvalidOperationException) 
       { 
        Console.WriteLine("Adding was compeleted!"); 
        break; 
       } 
       Console.WriteLine("Take:{0} ", i); 

       // Simulate a slow consumer. This will cause 
       // collection to fill up fast and thus Adds wil block. 
       Thread.SpinWait(100000); 
      } 

      Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit."); 
     }); 

     // A simple blocking producer with no cancellation. 
     Task.Factory.StartNew(() => 
     { 
      for (int i = 0; i < itemsToAdd; i++) 
      { 
       numbers.Add(i); 
       Console.WriteLine("Add:{0} Count={1}", i, numbers.Count); 
       numbers.Add(i); 
       Console.WriteLine("Add:{0} Count={1}", i, numbers.Count); 
      } 

      // See documentation for this method. 
      numbers.CompleteAdding(); 
     }); 

     // Keep the console display open in debug mode. 

     Console.ReadLine(); 
    } 
} 
} 
+0

Eiter某些東西非常壞或者你的測試代碼和輸出不匹配:在第一個Add()之後計數== 0? – 2011-05-01 11:56:00

+0

@亨克,他使用'BlockingCollection ',他將這個集合作爲構造函數的參數。所以當他調用Add()時,BlockingCollection 調用他的類的TryAdd()。 – svick 2011-05-01 14:23:55

+0

@svick,好的,謝謝,我錯過了。 – 2011-05-01 15:23:23

回答

3

BlockingCollection維護自己的計數,因此即使忽略重複項,計數增加不使用你的潛在ConcurrentSet的數。

您可能希望圍繞此集合編寫自己的包裝,該包裝從concurrentset中返回計數,並將每個其他方法屬性中繼到阻塞集合。

+0

除此之外,我的代碼一切正常?爲什麼BlockingCollection使用它自己的Count?這似乎完全沒用。 – javapowered 2011-05-02 07:33:49

+0

是的,你的代碼是好的。是的,保持自己的計數似乎是沒用的,但Eric Lippert可能會對此有所考慮。 – 2011-05-02 08:52:13