Reputation: 61
so i have a question about the System.Collections.Concurrent
I saw that the Concurrent is acctually a safe thread collection, but in wich cases it can be helpfull?
I made 2 examples and the result are the same
First the ConcurrentQueue:
static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
private static readonly object obj = new object();
static int i = 0;
static int Num = 0;
static void Run(object loopNum)
{
lock (obj)
{
for (int N = 0; N < 10; N++)
{
queue.Enqueue (i);
Thread.Sleep(250);
queue.TryDequeue(out Num);
Console.WriteLine($"{Num} Added! in {loopNum} Loop, ThreadID: [{Thread.CurrentThread.ManagedThreadId}]");
i++;
}
}
}
And now the normal Queue:
static Queue<int> queue = new Queue<int>();
private static readonly object obj = new object();
static int i = 0;
static void Run(object loopNum)
{
lock (obj)
{
for (int N = 0; N < 10; N++)
{
queue.Enqueue (i);
Thread.Sleep(250);
Console.WriteLine($"{queue.Dequeue()} Added! in {loopNum} Loop, ThreadID: [{Thread.CurrentThread.ManagedThreadId}]");
i++;
}
}
}
Main:
static void Main()
{
Thread[] Th = new Thread[] { new Thread(Run), new Thread(Run) };
Th[0].Start("First");
Th[1].Start("Second");
Console.ReadKey();
}
The result are the same
Sure, it got some diffrent methods like TryDequeue And a few more, but what it really helpfull for?
Any help will be very appriciated :)
Upvotes: 1
Views: 93
Reputation: 61
Thank you everyone for all your answers, really helped me out, i appriciate it alot.
By the way Matthew Watson, your example sometimes give's an exception and sometime's isnt, i made a better example, but yeah i get the point.
private static void Main()
{
var queue1 = new ConcurrentQueue<int>();
var queue2 = new Queue<int>();
// This will work fine.
var task1 = Enumerable.Range(0, 40)
.Select(_ => Task.Run(() => producer(item => queue1.Enqueue(item))))
.ToArray();
Task.WaitAll(task1);
// This will cause an exception.
var task2 = Enumerable.Range(0, 40)
.Select(_ => Task.Run(() => producer(item => queue2.Enqueue(item))))
.ToArray();
Task.WaitAll(task2);
}
Thanks again :)
Upvotes: 0
Reputation: 1274
When you are using the lock
construct, your code effectively executes in sequence, not in parallel. This solution is suitable for the version with simple Queue
as it's not thread-safe, but with ConcurrentQueue
, using lock
kinda defeats the purpose. Remove the lock for ConcurrentQueue
, remove the Thread.Sleep
, and use 20 threads instead of 2 just for kicks. You can use Parallel.For()
method to spawn your tasks.
Parallel.For(0, 20, i => Run());
Upvotes: 1
Reputation: 109852
The reason for using ConcurrentQueue<T>
is to avoid writing your own locking code.
If you have multiple threads adding or removing items from a Queue<T>
you are likely to get an exception. Using a ConcurrentQueue<T>
will avoid the exceptions.
Here's a sample program which will likely cause an exception when using multiple threads to write to a Queue<T>
while it works with a ConcurrentQueue<T>
:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
private static void Main()
{
var queue1 = new ConcurrentQueue<int>();
var queue2 = new Queue<int>();
// This will work fine.
var task1 = Task.Run(() => producer(item => queue1.Enqueue(item)));
var task2 = Task.Run(() => producer(item => queue1.Enqueue(item)));
Task.WaitAll(task1, task2);
// This will cause an exception.
var task3 = Task.Run(() => producer(item => queue2.Enqueue(item)));
var task4 = Task.Run(() => producer(item => queue2.Enqueue(item)));
Task.WaitAll(task3, task4);
}
private static void producer(Action<int> add)
{
for (int i = 0; i < 10000; ++i)
add(i);
}
}
Try running it and see what happens.
Upvotes: 2
Reputation:
Don't use lock()
in conjunction with ConcurrentQueue<>
or similar items in that namespace. It's detrimental to performance.
You can use ConcurrentQueue<>
safely with multiple threads and have great performance. The same can not be said with lock()
and regular collections.
That's why your results are the same.
Upvotes: 2