To interesujące pytanie. Po raz pierwszy widziałem, jak ktoś prosi o kolejkę blokującą, która ignoruje duplikaty. Co dziwne, nie mogłem znaleźć niczego, co chciałbyś, aby już istniało w BCL. Twierdzę, że jest to dziwne, ponieważ BlockingCollection
może zaakceptować IProducerConsumerCollection
jako kolekcję leżącą u podstaw, która ma metodę TryAdd
, która jest reklamowana jako niezdolna do pracy po wykryciu duplikatów. Problem polega na tym, że nie widzę konkretnej implementacji IProducerConsumerCollection
, która zapobiega duplikatom. Przynajmniej możemy napisać własną.
public class NoDuplicatesConcurrentQueue<T> : IProducerConsumerCollection<T>
{
// TODO: You will need to fully implement IProducerConsumerCollection.
private Queue<T> queue = new Queue<T>();
public bool TryAdd(T item)
{
lock (queue)
{
if (!queue.Contains(item))
{
queue.Enqueue(item);
return true;
}
return false;
}
}
public bool TryTake(out T item)
{
lock (queue)
{
item = null;
if (queue.Count > 0)
{
item = queue.Dequeue();
}
return item != null;
}
}
}
Teraz mamy IProducerConsumerCollection
że nie akceptuje duplikatów możemy użyć go tak:
public class Example
{
private BlockingCollection<object> queue = new BlockingCollection<object>(new NoDuplicatesConcurrentQueue<object>());
public Example()
{
new Thread(Consume).Start();
}
public void Produce(object item)
{
bool unique = queue.TryAdd(item);
}
private void Consume()
{
while (true)
{
object item = queue.Take();
}
}
}
nie lubić moje wykonanie NoDuplicatesConcurrentQueue
. Z pewnością możesz zaimplementować własne przy użyciu ConcurrentQueue
lub cokolwiek, jeśli uważasz, że potrzebujesz wydajności o niskim poziomie blokady, którą zapewniają kolekcje TPL.
Aktualizacja:
udało mi się przetestować kod rano. Jest dobra wiadomość i złe wieści. Dobra wiadomość jest taka, że to technicznie zadziała. Zła wiadomość jest taka, że prawdopodobnie nie będziesz chciał tego robić, ponieważ BlockingCollection.TryAdd
przechwytuje wartość zwracaną z podstawowej metody IProducerConsumerCollection.TryAdd
i zgłasza wyjątek po wykryciu false
. Tak, to prawda. Nie powraca on tak, jak można się spodziewać, a zamiast tego generuje wyjątek. Muszę być szczery, to jest zarówno zaskakujące, jak i śmieszne. Cały punkt metod TryXXX polega na tym, że nie powinni oni rzucać wyjątkami. Jestem głęboko rozczarowany.
Gdyby istniała taka metoda, musiałaby to być pojedyncza metoda atomowa. Nigdy nie można go zaimplementować, ponieważ twoje dwa fragmenty są spowodowane tym, że między TryPeek i Add może dodawać się wątek. Myślę, że nie masz szczęścia. Dlaczego i tak potrzebujesz takiej możliwości? –
w moim przypadku jest ok, jeśli inny wątek dodaje między trypeek i dodać. W większości robię to jako kontrolę bezpieczeństwa, w której program planujący uruchamia generowanie raportów na wielu wątkach (stąd wielu producentów). Jeśli program planujący działa nieprawidłowo z dowolnego powodu i uruchamia ten sam wyzwalacz wiele razy w krótkim przedziale, po prostu chciałbym go obsłużyć. Rozumiem, że mogę obejść i poradzić sobie z tym w jakiś sposób. Wygląda na to, że nie ma eleganckiego sposobu radzenia sobie z tym. dziękuję – Gullu