Custom thread pool C# issue [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
So, I have a custom thread pool which receives a maximum number of threads and you can queue items in it. The pool will execute the items in queue. The problem is that the behaviour of this pool is always different. I have created a test that gives X number of operations to this pool and waits for the pool to finish the work (there is a limit for this waiting, but the limit is enough for all the operations to end successfully). The problem is that sometime the test returns a "success" response but most of the times it exceeds it's time limit and/or does not process all the operations.
Code:
CustomThreadPool.cs
public class CustomThreadPool : IDisposable
{
#region Private Members
private readonly Thread m_checkThread;
#endregion
#region Public Properties
public int MaxNumberOfThreads { get; set; }
private readonly object m_lock = new object();
private int m_currentNumberOfThreads;
public int CurrentNumberOfThreads
{
get
{
lock (m_lock)
{
return m_currentNumberOfThreads;
}
}
private set
{
lock (m_lock)
{
m_currentNumberOfThreads = value;
}
}
}
public ConcurrentQueue<WorkItem> QueuedItems { get; set; }
#endregion
#region Constructor
public CustomThreadPool(int maxNumberOfThreads)
{
MaxNumberOfThreads = maxNumberOfThreads;
QueuedItems = new ConcurrentQueue<WorkItem>();
m_checkThread = new Thread(CheckThread);
m_checkThread.Start();
}
#endregion
#region Public Methods
public void QueueItem(object argument, Action<WorkItem> method, string token = "")
{
QueuedItems.Enqueue(new WorkItem { Argument = argument, Method = method, Token = token });
}
public List<WorkItem> Stop()
{
m_checkThread.Abort();
List<WorkItem> result = new List<WorkItem>();
while (QueuedItems.Count > 0)
{
WorkItem wi;
QueuedItems.TryDequeue(out wi);
if (wi != null)
result.Add(wi);
}
CurrentNumberOfThreads = 0;
return result;
}
#endregion
#region Private Methods
// ReSharper disable once FunctionNeverReturns
private void CheckThread()
{
while (true)
{
if (CurrentNumberOfThreads >= MaxNumberOfThreads || QueuedItems.Count == 0)
{
Thread.Yield();
}
int availableThreads = MaxNumberOfThreads - CurrentNumberOfThreads;
List<WorkItem> toBeProcessed = new List<WorkItem>();
for (var i = 0; i < availableThreads; i++)
{
WorkItem wi;
QueuedItems.TryDequeue(out wi);
if (wi != null)
{
toBeProcessed.Add(wi);
}
}
foreach (WorkItem item in toBeProcessed)
{
CurrentNumberOfThreads++;
item.ExecutingThread = new Thread(ProcessItem);
item.ExecutingThread.Start(item);
}
Thread.Sleep(50);
}
}
private void ProcessItem(object wi)
{
WorkItem item = (WorkItem)wi;
item.Method.Invoke(item);
CurrentNumberOfThreads--;
item.ExecutingThread.Abort();
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
m_checkThread.Abort();
}
}
}
Test:
private List<int> EndedOperations = new List<int>();
private List<int> StartedOperations = new List<int>();
public void CheckThreadPool()
{
int workTime = 100;
int numberOfOperations = 100;
int numberOfThreads = 10;
int cycles = numberOfOperations / numberOfThreads;
int totalTime = workTime * 2 * cycles;
Stopwatch sw = new Stopwatch();
sw.Start();
CustomThreadPool pool = new CustomThreadPool(numberOfThreads);
for (int i = 0; i < numberOfOperations; i++)
{
pool.QueueItem(new WorkInfo(i, workTime), DoWork);
}
Thread.Sleep(workTime);
bool queueEmpty = false, operationsDone = false;
while (pool.CurrentNumberOfThreads > 0 && sw.ElapsedMilliseconds < totalTime)
{
if (pool.QueuedItems.Count == 0 && !queueEmpty)
{
queueEmpty = true;
Debug.WriteLine("Queue emptied at: {0}, operations left: {1}", sw.ElapsedMilliseconds, numberOfOperations - EndedOperations.Count);
}
if (EndedOperations.Count == numberOfOperations && !operationsDone)
{
operationsDone = true;
Debug.WriteLine("Operations done at: {0}, number of threads: {1}", sw.ElapsedMilliseconds, pool.CurrentNumberOfThreads);
}
Thread.Yield();
}
sw.Stop();
pool.Dispose();
Thread.Sleep(workTime);
Debug.WriteLine("Test ended with {0} unprocessed operations", numberOfOperations - EndedOperations.Count);
for (int i = 0; i < numberOfOperations; i++)
{
if (!EndedOperations.Contains(i))
Debug.WriteLine("Operation {0} was not fully processed", i);
if (!StartedOperations.Contains(i))
Debug.WriteLine("Operation {0} has never started", i);
}
Assert.IsTrue(sw.ElapsedMilliseconds < totalTime,
string.Format(#"The pool did not stop in useful time.
Remaining threads : {0}
Remaining queued items : {1}
Remaining operations: {2}",
pool.CurrentNumberOfThreads, pool.QueuedItems.Count, numberOfOperations - EndedOperations.Count));
Assert.IsTrue(pool.QueuedItems.Count == 0,
string.Format(#"Not all items were processed.
Remaining : {0}
Processing time : {1}",
pool.QueuedItems.Count, sw.ElapsedMilliseconds));
}
private void DoWork(WorkItem wi)
{
WorkInfo info = (WorkInfo)wi.Argument;
try
{
StartedOperations.Add(info.Id);
Thread.Sleep(info.TestTime);
EndedOperations.Add(info.Id);
}
catch(Exception ex)
{
Debug.WriteLine("id: {0}, ex: {1}", info.Id, ex.Message);
}
}
I'm assuming that the problem might come from a shared resource but I can't figure out which one.
Thank you.

Even though you put a lock on the assignment of CurrentNumberOfThreads, that is no enough because your increment and decrement operations are not atomic.
You should use atomic operations provided by Interlocked class:
Replace
CurrentNumberOfThreads++;
With
System.Threading.Interlocked.Increment(ref m_CurrentNumberOfThreads);
and
CurrentNumberOfThreads--;
with
System.Threading.Interlocked.Decrement(ref m_CurrentNumberOfThreads);
And another thing to notice: never abort the threads like this. Try to make some way for them to be informed and exit gracefully.

Related

How property actually manages to return Priority?

Here is the piece of code which represents so-called wrapper class PriorityQueue which contains three ordinary queues of different priority (High, Medium and Low) and tied with Enumeration Priority to which class Package () is connected via it's read-only property by the name Priority. And the Priority is being assigned via CreatePackage() method of PackageFactory class which creates objects (instances) of class Package (Package class have a constructor which requires to point it's _priority member in parentheses) using Random class and cast from int to Priority. This is the general background. Code is following below:
namespace PriorityQueue
{
enum Priority
{
High,
Medium,
Low
}
interface IPrioritizable
{
Priority Priority { get; }
}
class PriorityQueue<T> where T:IPrioritizable
{
private Queue<T> _queueHigh = new Queue<T>();
private Queue<T> _queueMedium = new Queue<T>();
private Queue<T> _queueLow = new Queue<T>();
public void Enqueue(T item)
{
switch (item.Priority)
{
case PriorityQueue.Priority.High:
_queueHigh.Enqueue(item);
break;
case PriorityQueue.Priority.Medium:
_queueMedium.Enqueue(item);
break;
case PriorityQueue.Priority.Low:
_queueLow.Enqueue(item);
break;
default:
throw new ArgumentOutOfRangeException(item.Priority.ToString(), "Bad priority in Priority queue. ");
}
}
public T Dequeue()
{
Queue<T> queueTop = TopQueue();
if (queueTop != null & queueTop.Count > 0)
{
return queueTop.Dequeue();
}
return default(T);
}
private Queue<T> TopQueue()
{
if (_queueHigh != null & _queueHigh.Count > 0) return _queueHigh;
if (_queueMedium != null & _queueMedium.Count > 0) return _queueMedium;
if (_queueLow != null & _queueLow.Count > 0) return _queueLow;
return _queueLow;
}
public int Count ()
{
return _queueHigh.Count + _queueMedium.Count + _queueLow.Count;
}
public bool IsEmpty()
{
return (_queueHigh.Count == 0) & (_queueMedium.Count == 0) & (_queueLow.Count == 0);
}
}
class Package:IPrioritizable
{
private Priority _priority;
public Package(Priority priority)
{
_priority = priority;
}
public Priority Priority => _priority;
}
class PackageFactory
{
Random _randgen = new Random();
public Package CreatePackage()
{
int numOfPriority = _randgen.Next(3);
return new Package((Priority)numOfPriority);
}
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Creating PriorityQueue...");
PriorityQueue<Package> pq = new PriorityQueue<Package>();
Package pack;
PackageFactory fact = new PackageFactory();
Random rnd = new Random();
Console.WriteLine("\nNow we are going to create random number of 0-20 range as a number of packets to be created. ");
int numsToCreate = rnd.Next(20);
Console.WriteLine("\nAnd now we are creating {0} packages: ", numsToCreate);
for (int i = 0; i < numsToCreate; i++)
{
Console.Write("\tPackage {0} ", i);
pack = fact.CreatePackage();
Console.WriteLine("priority is: {0} ", pack.Priority);
pq.Enqueue(pack);
}
int total = pq.Count();
Console.WriteLine("\nTotal number of packages received is: {0}", total);
Console.WriteLine("\nNow we are going to create random number of 0-20 range as a number of packets to be removed. ");
int numsToRemove = rnd.Next(20);
Console.WriteLine("\nAnd now we are removing up to {0} packages: ", numsToRemove);
for (int i = 0; i < numsToRemove; i++)
{
pack = pq.Dequeue();
if(pack != null)
{
Console.WriteLine("Removed (shipped) package with priority: {0} ", pack.Priority);
}
}
Console.WriteLine("\nNumber of packages removed (shipped) is: {0}", total - pq.Count());
Console.WriteLine("\nPress any key to terminate program...");
Console.ReadKey();
}
}
}
Also there are customised Enqueue() and Dequeue() methods for PriorityQueue class. Actually what I don't understand is the Main() part of the program where we are using customised Dequeue() method to remove instances of Package class. How does the program learns of removed package via Priority property after customised Dequeue() method being used? Object was removed and how it can learn it Priority? Does it have to do something with reference types? Here is the part which I question:
for (int i = 0; i < numsToRemove; i++)
{
pack = pq.Dequeue();
if(pack != null)
{
Console.WriteLine("Removed (shipped) package with priority: {0} ", pack.Priority);
}
}

Parallelism and Async C#

I'm writing a Console Application in C# that takes an array of videos and transcode it as long a new GPU is free to use.
The machine where the app will run has two GPUs. But I'm really having a hard time how to build this up.
The method that does the job is FireTranscode()
private void FireTranscode(int counter)
{
Random rand = new Random();
int gpu;
lock (thisLock)
{
gpu = PickGPU(0) == true ? 0 : 1;
GPU[gpu] = false;
if (gpu == 0) { gpuZero += 1; } else { gpuOne += 1; };
Thread.Sleep(rand.Next(1, 5));
videos -= 1;
}
Console.WriteLine($"Transconding on {gpu} using thread: {Thread.CurrentThread.ManagedThreadId} {transcodeArray[Convert.ToInt32(counter), 2]}");
GPU[gpu] = true;
}
and it's triggered by ManageTranscode()
private async void ManageTrancode()
{
for(counter=0; counter < videos; counter++)
{
if (GPU[0] == false & GPU[1] == false)
{
await Task.WhenAny(transcodeList);
}
else
{
transcodeList.Add(Task.Factory.StartNew(() => FireTranscode(counter)));
}
}
}
It suppose to call the FireTranscode followed by the parameter counter, 40 times async (value of videos variable), and in case both GPU (static Dictionary<int, bool> GPU = new Dictionary<int, bool> { { 0, true }, { 1, true } }; are in use (=false) it should wait until any task finishes and free for use (=true).
I'm trying to learn how to use it correctly and I would appreciate some tips and help how to achieve this.
Thank you.
You can simplify your logic and also make it more extensible in terms of available GPU by using below code. It uses SemaphoreSlim (also mentioned by #Poul Bak) which allows degree of parallelism by defined parameters.
Also, I've refactored your code to have GPU as class (you can use Struct too).
private object lockObj = new object();
private List<GPU> availableGPUs = List<GPU>() { /* initialize GPUs here */};
private int AvailableGPUCount { get { return availableGPUs.Count(); } }
private async void ManageTrancode()
{
int maxThread = AvailableGPUCount;
SemaphoreSlim lockSlim = new SemaphoreSlim(maxThread, maxThread);
for(int counter = 0; counter < videos; counter++)
{
await lockSlim.WaitAsync();
await Trancode();
lockSlim.Release();
}
}
private async Task Trancode()
{
GPU gpu = GetAndLockGPU();
// await <<Run actual trancode here>>
ReleaseGPU(gup.Id);
}
private GPU GetAndLockGPU()
{
GPU gpu = null;
lock (lockObj)
{
gpu = availableGPUs.First(g => g.IsAvailable);
gpu.IsAvailable = false;
}
return gpu;
}
private void ReleaseGPU(int gpuId)
{
lock (lockObj)
{
availableGPUs.First(g => g.Id == gpuId).IsAvailable = true;
}
}
private class GPU
{
public int Id {get; set;}
public bool IsAvailable {get; set;} = true;
}

C# Immutable counter for multiple fields

I have a fairly high throughput on a message counter (tens of thousands per second), and looking for an efficient way of getting the count without putting locks everywhere or ideally not locking on each message count when i am giving an update every 10 seconds.
Use of immutable counter object
I am using an immutable counter class:
public class Counter
{
public Counter(int quotes, int trades)
{
Quotes = quotes;
Trades = trades;
}
readonly public int Quotes;
readonly public int Trades;
// and some other counter fields snipped
}
And would update this on each message process loop:
class MyProcessor
{
System.Timers.Timer timer;
Counter counter = new Counter(0,0);
public MyProcessor()
{
// update ever 10 seconds
this.timer = new System.Timers.Timer(10000);
timer.Elapsed += (sender, e) => {
var quotesPerSecond = this.counter.Quotes / 10.0;
var tradesPerSecond = this.counter.Trades / 10.0;
this.Counter = new Counter(0,0);
});
}
public void ProcessMessages(Messages messages)
{
foreach(var message in messages) { /* */ }
var oldCounter = counter;
this.counter = new Counter(oldCounter.Quotes, oldCounter.Trades);
}
}
I have lots of counters (not all shown), so would mean a lot of individual Interlocked.Increment calls on individual counter fields.
The only other way I can think of is lock every single run of ProcessMessages (which will be extensive) and heavy for something which is a utility as opposed to critical where the program would crash.
Is it possible to use an immutable counter object in this fashion without hard interlocking/thread mechanisms when we only need to update once every 10 seconds?
Flag check idea to avoid locks
Could the timer thread set a flag for the ProcessMessages to check and if it sees it set, start the count from zero again, i.e.
/* snipped the MyProcessor class, same as before */
System.Timers.Timer timer;
Counter counter = new Counter(0,0);
ManualResetEvent reset = new ManualResetEvent(false);
public MyProcessor()
{
// update ever 10 seconds
this.timer = new System.Timers.Timer(10000);
timer.Elapsed += (sender, e) => {
var quotesPerSecond = this.counter.Quotes / 10.0;
var tradesPerSecond = this.counter.Trades / 10.0;
// log
this.reset.Set();
});
}
// this should be called every second with a heartbeat message posted to queue
public void ProcessMessages(Messages messages)
{
if (reset.WaitOne(0) == true)
{
this.counter = new Counter(this.counter.Quotes, this.counter.Trades, this.counter.Aggregates);
reset.Reset();
}
else
{
this.counter = new Counter(
this.counter.Quotes + message.Quotes.Count,
this.counter.Trades + message.Trades.Count);
}
}
/* end of MyProcessor class */
This would work, however the update "stalls" when the process messages comes to a halt (although the throughput is very high, it does pause for a number of hours at night ideally should show the actual rather than last value).
One way around this would be to post a heartbeat message to the MyProcessor.ProcessMessages() every second to force an internal update of the message counters and subsequent reset when the reset ManualResetEvent is set.
Here are three new methods for your Counter class. One for reading the latest value from a specific location, one for updating safely a specific location, and one for creating easily a new Counter based on an existing one:
public static Counter Read(ref Counter counter)
{
return Interlocked.CompareExchange(ref counter, null, null);
}
public static void Update(ref Counter counter, Func<Counter, Counter> updateFactory)
{
var counter1 = counter;
while (true)
{
var newCounter = updateFactory(counter1);
var counter2 = Interlocked.CompareExchange(ref counter, newCounter, counter1);
if (counter2 == counter1) break;
counter1 = counter2;
}
}
public Counter Add(int quotesDelta, int tradesDelta)
{
return new Counter(Quotes + quotesDelta, Trades + tradesDelta);
}
Usage example:
Counter latest = Counter.Read(ref this.counter);
Counter.Update(ref this.counter, existing => existing.Add(1, 1));
Accessing the MyProcessor.counter field directly by multiple threads concurrently is not thread-safe, because it's neither volatile nor protected by a lock. The above methods are safe to use because they are accessing the field through interlocked operations.
I wanted to update everyone with what I had come up with, the counter updates were pushed within the thread itself.
Everything is driven by the DequeueThread loop, and specifically this.queue.ReceiveAsync(TimeSpan.FromSeconds(UpdateFrequencySeconds)) function.
This will either return an item from the queue, process it and update the counters, or timeout and then update the counters - there are no other threads involved everything, including updating message rate, is done within the thread.
In summary, nothing runs in parallel (in terms of dequing the packet), it is fetching the items one at a time and processing it and the counters thereafter. Then finally looping back to process the next item in the queue.
This removes the need for synchronisation:
internal class Counter
{
public Counter(Action<int,int,int,int> updateCallback, double updateEvery)
{
this.updateCallback = updateCallback;
this.UpdateEvery = updateEvery;
}
public void Poll()
{
if (nextUpdate < DateTimeOffset.UtcNow)
{
// post the stats, and reset
this.updateCallback(this.quotes, this.trades, this.aggregates, this.statuses);
this.quotes = 0;
this.trades = 0;
this.aggregates = 0;
this.statuses = 0;
nextUpdate = DateTimeOffset.UtcNow.AddSeconds(this.UpdateEvery);
}
}
public void AddQuotes(int count) => this.quotes += count;
public void AddTrades(int count) => this.trades += count;
public void AddAggregates(int count) => this.aggregates += count;
public void AddStatuses(int count) => this.statuses += count;
private int quotes;
private int trades;
private int aggregates;
private int statuses;
private readonly Action<int,int,int,int> updateCallback;
public double UpdateEvery { get; private set; }
private DateTimeOffset nextUpdate;
}
public class DeserializeWorker
{
private readonly BufferBlock<byte[]> queue = new BufferBlock<byte[]>();
private readonly IPolygonDeserializer polygonDeserializer;
private readonly ILogger<DeserializeWorker> logger;
private readonly Counter counter;
const double UpdateFrequencySeconds = 5.0;
long maxBacklog = 0;
public DeserializeWorker(IPolygonDeserializer polygonDeserializer, ILogger<DeserializeWorker> logger)
{
this.polygonDeserializer = polygonDeserializer ?? throw new ArgumentNullException(nameof(polygonDeserializer));
this.logger = logger;
this.counter = new Counter(ProcesCounterUpdateCallback, UpdateFrequencySeconds);
}
public void Add(byte[] data)
{
this.queue.Post(data);
}
public Task Run(CancellationToken stoppingToken)
{
return Task
.Factory
.StartNew(
async () => await DequeueThread(stoppingToken),
stoppingToken,
TaskCreationOptions.LongRunning,
TaskScheduler.Default)
.Unwrap();
}
private async Task DequeueThread(CancellationToken stoppingToken)
{
while (stoppingToken.IsCancellationRequested == false)
{
try
{
var item = await this.queue.ReceiveAsync(TimeSpan.FromSeconds(UpdateFrequencySeconds), stoppingToken);
await ProcessAsync(item);
}
catch (TimeoutException)
{
// this is ok, timeout expired
}
catch(TaskCanceledException)
{
break; // task cancelled, break from loop
}
catch (Exception e)
{
this.logger.LogError(e.ToString());
}
UpdateCounters();
}
await StopAsync();
}
protected async Task StopAsync()
{
this.queue.Complete();
await this.queue.Completion;
}
protected void ProcessStatuses(IEnumerable<Status> statuses)
{
Parallel.ForEach(statuses, (current) =>
{
if (current.Result != "success")
this.logger.LogInformation($"{current.Result}: {current.Message}");
});
}
protected void ProcessMessages<T>(IEnumerable<T> messages)
{
Parallel.ForEach(messages, (current) =>
{
// serialize by type T
// dispatch
});
}
async Task ProcessAsync(byte[] item)
{
try
{
var memoryStream = new MemoryStream(item);
var message = await this.polygonDeserializer.DeserializeAsync(memoryStream);
var messagesTask = Task.Run(() => ProcessStatuses(message.Statuses));
var quotesTask = Task.Run(() => ProcessMessages(message.Quotes));
var tradesTask = Task.Run(() => ProcessMessages(message.Trades));
var aggregatesTask = Task.Run(() => ProcessMessages(message.Aggregates));
this.counter.AddStatuses(message.Statuses.Count);
this.counter.AddQuotes(message.Quotes.Count);
this.counter.AddTrades(message.Trades.Count);
this.counter.AddAggregates(message.Aggregates.Count);
Task.WaitAll(messagesTask, quotesTask, aggregatesTask, tradesTask);
}
catch (Exception e)
{
this.logger.LogError(e.ToString());
}
}
void UpdateCounters()
{
var currentCount = this.queue.Count;
if (currentCount > this.maxBacklog)
this.maxBacklog = currentCount;
this.counter.Poll();
}
void ProcesCounterUpdateCallback(int quotes, int trades, int aggregates, int statuses)
{
var updateFrequency = this.counter.UpdateEvery;
logger.LogInformation(
$"Queue current {this.queue.Count} (max {this.maxBacklog }), {quotes / updateFrequency} quotes/sec, {trades / updateFrequency} trades/sec, {aggregates / updateFrequency} aggregates/sec, {statuses / updateFrequency} status/sec");
}
}

Threads monitoring a Queue<Actions>

I doing a small project to map a network (routers only) using SNMP. In order to speed things up, I´m trying to have a pool of threads responsible for doing the jobs I need, apart from the first job which is done by the main thread.
At this time I have two jobs, one takes a parameter the other doesn´t:
UpdateDeviceInfo(NetworkDevice nd)
UpdateLinks() *not defined yet
What I´m trying to achieve is to have those working threads waiting for a job to
appear on a Queue<Action> and wait while it is empty. The main thread will add the first job and then wait for all workers, which might add more jobs, to finish before starting adding the second job and wake up the sleeping threads.
My problem/questions are:
How to define the Queue<Actions> so that I can insert the methods and the parameters if any. If not possible I could make all functions accept the same parameter.
How to launch the working threads indefinitely. I not sure where should I create the for(;;).
This is my code so far:
public enum DatabaseState
{
Empty = 0,
Learning = 1,
Updating = 2,
Stable = 3,
Exiting = 4
};
public class NetworkDB
{
public Dictionary<string, NetworkDevice> database;
private Queue<Action<NetworkDevice>> jobs;
private string _community;
private string _ipaddress;
private Object _statelock = new Object();
private DatabaseState _state = DatabaseState.Empty;
private readonly int workers = 4;
private Object _threadswaitinglock = new Object();
private int _threadswaiting = 0;
public Dictionary<string, NetworkDevice> Database { get => database; set => database = value; }
public NetworkDB(string community, string ipaddress)
{
_community = community;
_ipaddress = ipaddress;
database = new Dictionary<string, NetworkDevice>();
jobs = new Queue<Action<NetworkDevice>>();
}
public void Start()
{
NetworkDevice nd = SNMP.GetDeviceInfo(new IpAddress(_ipaddress), _community);
if (nd.Status > NetworkDeviceStatus.Unknown)
{
database.Add(nd.Id, nd);
_state = DatabaseState.Learning;
nd.Update(this); // The first job is done by the main thread
for (int i = 0; i < workers; i++)
{
Thread t = new Thread(JobRemove);
t.Start();
}
lock (_statelock)
{
if (_state == DatabaseState.Learning)
{
Monitor.Wait(_statelock);
}
}
lock (_statelock)
{
if (_state == DatabaseState.Updating)
{
Monitor.Wait(_statelock);
}
}
foreach (KeyValuePair<string, NetworkDevice> n in database)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(n.Value.Name + ".txt")
{
file.WriteLine(n);
}
}
}
}
public void JobInsert(Action<NetworkDevice> func, NetworkDevice nd)
{
lock (jobs)
{
jobs.Enqueue(item);
if (jobs.Count == 1)
{
// wake up any blocked dequeue
Monitor.Pulse(jobs);
}
}
}
public void JobRemove()
{
Action<NetworkDevice> item;
lock (jobs)
{
while (jobs.Count == 0)
{
lock (_threadswaitinglock)
{
_threadswaiting += 1;
if (_threadswaiting == workers)
Monitor.Pulse(_statelock);
}
Monitor.Wait(jobs);
}
lock (_threadswaitinglock)
{
_threadswaiting -= 1;
}
item = jobs.Dequeue();
item.Invoke();
}
}
public bool NetworkDeviceExists(NetworkDevice nd)
{
try
{
Monitor.Enter(database);
if (database.ContainsKey(nd.Id))
{
return true;
}
else
{
database.Add(nd.Id, nd);
Action<NetworkDevice> action = new Action<NetworkDevice>(UpdateDeviceInfo);
jobs.Enqueue(action);
return false;
}
}
finally
{
Monitor.Exit(database);
}
}
//Job1 - Learning -> Update device info
public void UpdateDeviceInfo(NetworkDevice nd)
{
nd.Update(this);
try
{
Monitor.Enter(database);
nd.Status = NetworkDeviceStatus.Self;
}
finally
{
Monitor.Exit(database);
}
}
//Job2 - Updating -> After Learning, create links between neighbours
private void UpdateLinks()
{
}
}
Your best bet seems like using a BlockingCollection instead of the Queue class. They behave effectively the same in terms of FIFO, but a BlockingCollection will let each of your threads block until an item can be taken by calling GetConsumingEnumerable or Take. Here is a complete example.
http://mikehadlow.blogspot.com/2012/11/using-blockingcollection-to-communicate.html?m=1
As for including the parameters, it seems like you could use closure to enclose the NetworkDevice itself and then just enqueue Action instead of Action<>

How to monitor/wait on array of objects?

I am using concurrent bag to store a set of objects. I want to implement something like
if(an object is present)
return it
else wait until one get free, if it does not get free in a specific time throw an exception.
if(object has been returned)
add to bag
I was thinking to use monitors but monitor can wait on a specific object. I want to wait till any of them is free. How can I implement it?
Extending the msdn example found here:
public class FiniteObjectPool<T>: IDisposable
{
System.Threading.AutoResetEvent m_Wait = new System.Threading.AutoResetEvent(false);
private ConcurrentBag<T> _objects;
public FiniteObjectPool()
{
_objects = new ConcurrentBag<T>();
}
public T GetObject()
{
T item;
while(!_objects.TryTake(out item))
{
m_Wait.WaitOne(); //an object was not available, wait until one is
}
return item;
}
public void PutObject(T item)
{
_objects.Add(item);
m_Wait.Set(); //signal a waiting thread that object may now be available
}
public void Dispose()
{
m_Wait.Dispose();
}
}
EDIT - example usage with 'Context' idiom wrapper
class Program
{
public class FiniteObjectPoolContext<T>: IDisposable
{
FiniteObjectPool<T> m_Pool = new FiniteObjectPool<T>();
public T Value { get; set; }
public FiniteObjectPoolContext(FiniteObjectPool<T> pool)
{
m_Pool = pool;
Value = pool.GetObject(); //take an object out - this will block if none is available
}
public void Dispose()
{
m_Pool.PutObject(Value); //put the object back because this context is finished
}
}
static void Main(string[] args)
{
FiniteObjectPool<int> pool = new FiniteObjectPool<int>();
for (int i = 0; i < 10; i++)
{
pool.PutObject(i);
}
List<Task> tasks = new List<Task>();
for (int i = 0; i < 20; i++)
{
int id = i;
tasks.Add(Task.Run(() =>
{
Console.WriteLine("Running task " + id);
using (var con = new FiniteObjectPoolContext<int>(pool))
{
Console.WriteLine("Task " + id + " got object from pool: " + con.Value);
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Task " + id + " is finished with pool object: " + con.Value);
}
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("DONE");
Console.ReadLine();
}
}
Notice the latency injected by the thread synchronization mechanisms.
Try Semaphores for the operations that you want to do. .NET has two implementations for Semaphores. Semaphore and SemaphoreSlim, both can be used to implement many threads trying to access a pool of resources.

Categories