Thread Pool with BlockingCollection - c#

Problem: There are multiple threads accessing a resource. I need to limit their number to a constant MaxThreads. Threads who cannot enter the thread pool should get an error message.
Solution: I started using a BlockingCollection<string> pool in the algorithm below, but I see that BlockingCollection requires a call to CompleteAdding, which I can't do, because I always get incoming threads (I hardcoded to 10 in the example below for debugging purposes), think web requests.
public class MyTest {
private const int MaxThreads = 3;
private BlockingCollection<string> pool;
public MyTest() {
pool = new BlockingCollection<string>(MaxThreads);
}
public void Go() {
var addSuccess = this.pool.TryAdd(string.Format("thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
if (!addSuccess) Console.WriteLine(string.Format("thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
Console.WriteLine(string.Format("Adding thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
Console.WriteLine(string.Format("Pool size: {0}", pool.Count));
// simulate work
Thread.Sleep(1000);
Console.WriteLine("Thread ID#{0} " + Thread.CurrentThread.ManagedThreadId + " is done doing work.");
string val;
var takeSuccess = this.pool.TryTake(out val);
if (!takeSuccess) Console.WriteLine(string.Format("Failed to take out thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
Console.WriteLine("Taking out " + val);
Console.WriteLine(string.Format("Pool size: {0}", pool.Count));
Console.WriteLine(Environment.NewLine);
}
}
static void Main()
{
var t = new MyTest();
Parallel.For(0, 10, x => t.Go());
}
Any ideas on how I can better achieve this?
Thanks!
P.S. Multi-threading newbie here, if you have any suggestions for reading materials, I would greatly appreciate them.
LE: Based on the answers I got, I was able to achieve the desired behavior using this algorithm:
public class MyTest {
private const int MaxThreads = 3;
private SemaphoreSlim semaphore;
public MyTest() {
semaphore = new SemaphoreSlim(MaxThreads, MaxThreads);
}
public void Go() {
Console.WriteLine(string.Format("In comes thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
semaphore.Wait();
try {
Console.WriteLine(string.Format("Serving thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
// simulate work
Thread.Sleep(1000);
Console.WriteLine(string.Format("Out goes thread ID#{0}", Thread.CurrentThread.ManagedThreadId));
}
finally {
semaphore.Release();
}
}
}
static void Main()
{
var t = new MyTest();
Parallel.For(0, 10, x=> t.Go());
}

If you want to protect certain number of threads which can access a critical region at a time, you'll have to use Semaphore or SemaphoreSlim. I suggest latter one, which is light weight when compared to former.
One disadvantage of SemaphoreSlim is that they won't work cross process, but that's fine we have Semaphore to help.
You can test whether the Semaphore is full via one of the Wait methods provided by the framework with a timeout.
SemaphoreSlim semaphore = new SemaphoreSlim(3, 3);
if (!semaphore.Wait(0))
{
//Already semaphore full.
//Handle it as you like
}
http://www.albahari.com/threading/ is a very good resource for threading.

Related

Μeasuring the time of a thread quantum [closed]

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 3 months ago.
Improve this question
I'm trying to measure the time each thread quantum takes using c#.
using System;
using System.Diagnostics;
using System.Threading;
using System.Collections.Concurrent;
namespace pr
{
public static class Program
{
private static Stopwatch watch = new Stopwatch();
private static long t = 0;
private static volatile int id = 0;
private static BlockingCollection<long> deltas = new();
public static void Main(string[] args)
{
ProcessAffinity();
PriorityClass();
ThreadDemo();
}
private static void ProcessAffinity()
{
Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)(1 << 0);
}
private static void PriorityClass()
{
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
}
private static void ThreadDemo()
{
var thread1 = new Thread(() => InsideThread())
{
IsBackground = true,
Priority = ThreadPriority.Highest
};
var thread2 = new Thread(() => InsideThread())
{
IsBackground = true,
Priority = ThreadPriority.Highest
};
watch.Start();
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
var avg = deltas.Average();
Console.WriteLine("Average: " + avg);
foreach (var e in deltas)
{
Console.WriteLine("delta: " + e);
}
}
private static void InsideThread()
{
while (true)
{
var currentId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("id" + currentId);
if (id == 0)
{
id = currentId;
}
if (id != currentId)
{
var newt = watch.ElapsedMilliseconds;
deltas.Add(newt - t);
t = newt;
id = currentId;
}
if (watch.ElapsedMilliseconds > 3000) break;
}
}
}
}
If I print the id of the current thread to the console, everything kind of works and the delta gets counted. But the Console.WriteLine changes the context (I guess? I'm new in this topic) and increases the time, so the result is not correct (it's around 100 ms, while it's supposed to be around 32 ms). However, if I comment the Console.WriteLine("id" + currentId); out, the threads don't change: thread2 seems to start only after thread1 completes its work, so the final delta is 3000ms + something. Increasing the time doesn't help too. My question is: why don't the threads run simultaneously when Console.WriteLine() is commented out? How to measure the time of a thread quantum correctly? Should I use any locks? (I've tried, but nothing changed)
My guess is that Console.WriteLine will yield the time slice, possibly due to blocking on a lock. This is not really unexpected.
Without yielding, the thread1 will have a higher priority than your "main" thread. So it is likely that thread2.Start(); will never be run before the thread1 has completed.
To get around this you could use a manualResetEvent. Let each thread wait on the same event, and set this from the main thread after both have been started. Waiting on the event will block the threads, letting the main thread run. When the event has been set, both threads should be eligible to run.
Note that I'm not at all sure that this will give any accurate results, but it should fix the current behavior you are seeing.

C# .NET Core 3.1 SemaphoreSlim with many threads and timeout: how does it work?

In the system I am working at, I have a REST Api available for multiple parallel requests.
In the background, I have a Thrift connection with another tool, allowing maximal 5 threads.
To protect the access to that connection, I am using a SemaphoreSlim object with also a configurable timeout. This is my code:
private static readonly SemaphoreSlim _lockGuard = new SemaphoreSlim(1, 5);
private static int _waitingThreads = 0;
public async Task<Models.OperationStatus> PushItemWithResultAsync<TPushData>(TPushData data, CancellationToken ct)
where TPushData : Thrift.Protocol.TBase
{
Interlocked.Increment(ref _waitingThreads);
// Use a semaphore to prevent parallel calls.
if (await _lockGuard.WaitAsync(_millisecondsThriftTimeout, ct))
{
_logger.LogDebug($"Push data using {_waitingThreads} threads, but {_lockGuard.CurrentCount} within the semaphore");
Models.OperationStatus result;
try
{
// push the data to the single Thrift connection.
result = await PushItemWithResultAsyncLogic(data, ct).ConfigureAwait(false);
}
finally
{
_lockGuard.Release();
Interlocked.Decrement(ref _waitingThreads);
}
return result;
}
else
{
Interlocked.Decrement(ref _waitingThreads);
return GetFailureOperationStatus();
}
}
I am a bit confused how to use the timeout of the semaphore and the allowed thread.
I first started with new SemaphoreSlim(1, 1); to allow only a single connection.
The code behaves as expected: only one call to PushItemWithResultAsyncLogic runs. If too many calls to the methods are coming, they will timeout.
Now, I want to max 5 running PushItemWithResultAsyncLogic in parallel, thus the max allowed thread in the SemaphoreSlim raised to 5.
I expect to still have possible timeout when a lot of external requests are coming.
If 10 parallel calls are received, the timeout of 500ms is still enough to handle all of them.
The _waitingThreads raises up to 10 but _lockGuard.CurrentCount is always 0.
I was expecting to have something moving between 1 and 5...
And most importantly, I don't see the below component to be called up to 5 times.
What is wrong here?
EDIT: short answer: the initialCount should be set to 5 as explained below.
So with new SemaphoreSlim(5, 5), I do have parallel requests AND the timeout playing its job.
Tried to simulate your scenario with the console app below and added some comments. The Wait method in the semaphore is not used to define a timeout. It is used to block a thread from entering until there is a free slot. Also a distinction needs to be made between allowing a given number of requests to wait and rejecting the others. In the simulation below, 5 is the max number of requests that can be processed in parallel. Others will immediately be rejected until a processing slot is free again.
As mentioned in the comments, this will not throttle. If you want to change the number of max requests processed in parallel, you need to 1. increase the maxCount in Semaphore constructor and 2. Release(maxCount) to reset the Semaphore. Or simply call the constructor with initialCount = maxCount, for ex. SemaphoreSlim(5, 5)
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Semaphore
{
public class Program
{
private static readonly SemaphoreSlim _lockGuard = new SemaphoreSlim(0, 5);
private static int totalProcessed; // Stats only.
public static void Main()
{
// Reset semaphore by allowing max 5 requests to enter the concurrent section.
_lockGuard.Release(5);
var requests = new Task[20];
for (var i = 0; i < 20; i++)
{
Console.WriteLine("Sending request: " + i);
var i1 = i;
requests[i] = Task.Run(() => ProcessRequest(i1));
// This is the interval between each request.
// Bigger value means more requests will be processed.
Thread.Sleep(20);
}
Task.WaitAll(requests);
Console.WriteLine("---");
Console.WriteLine("Processed requests: " + totalProcessed);
Console.WriteLine("Rejected requests: " + (20 - totalProcessed));
Console.ReadLine();
}
public static async Task ProcessRequest(int i)
{
// If the semaphore is already full, reject the request.
// This means requests won't get queued.
if (_lockGuard.CurrentCount == 0)
{
Console.WriteLine("ERROR: CurrentCount == 0 for " + i);
return;
}
try
{
// If this request was allowed to enter the semaphore, wait until a processing slot is free.
// This decrements CurrentCount.
_lockGuard.Wait();
// Once previous request completed (Release()), process the next waiting one.
await Task.Run(() => {
Console.WriteLine("Processing request " + i );
Thread.Sleep(300); // Simulate request processing time.
});
totalProcessed++;
}
finally
{
// Processing completed: release one slot in the semaphore.
_lockGuard.Release();
Console.WriteLine("Releasing " + i);
}
}
}
}
Note
As Kit said in the comments, you don't need a Semaphore to achieve what I understand you're trying to achieve. I assume there are no shared resources in PushItemWithResultAsyncLogic code, so what you need is effectively a counter and/or some request queue management if you want to allow some requests to wait.
EDIT
To process as many requests as possible and have them waiting for a timeout, there is no need to check CurrentCount. Simply Wait(x milliseconds) and either request will be processed with lock aquired or it will be rejected and Wait will return false.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Semaphore
{
public class Program
{
private static readonly SemaphoreSlim _lockGuard = new SemaphoreSlim(5, 5);
private static readonly Random rnd = new Random((int)DateTimeOffset.Now.Ticks);
private static int totalProcessed; // Stats
public static void Main()
{
var requests = new Task[20];
for (var i = 0; i < 20; i++)
{
var i1 = i;
requests[i] = Task.Run(() => ProcessRequest(i1));
Thread.Sleep(100);
}
Task.WaitAll(requests);
Console.WriteLine("----------------------------");
Console.WriteLine("Processed requests: " + totalProcessed);
Console.WriteLine("Rejected requests: " + (20 - totalProcessed));
Console.WriteLine("----------------------------");
Console.ReadLine();
}
public static async Task ProcessRequest(int i)
{
Console.WriteLine(i + " Wait");
// If at the end of the wait no slot is free, reject.
if (!await _lockGuard.WaitAsync(500))
{
Console.WriteLine(i + " Reject");
return;
}
try
{
// Once previous request completed (Release()), process the next waiting one.
await Task.Run(() =>
{
Console.WriteLine(i + " Process");
Thread.Sleep(rnd.Next(600, 800)); // Simulate random request processing time.
});
totalProcessed++;
}
finally
{
// Processing completed: release one slot in the semaphore.
_lockGuard.Release();
}
}
}
}
Set the initialCount constructor parameter to the same value as maxCount. CurrentCount goes from initialCount towards 0. So, if the initialCount is 1 then the first thread sets the CurrentCount to zero and prevents other threads from running.

how to transfer some info to other?

I have two threads. How to get data from thread1 to thread2. It means, whe thread1 has done its work, it has some data, and this data must be used in the second "thread2". How to realize it ?
Here is code, but what to do..now ?
static void Main(string[] args)
{
Thread t1 = new Thread(thread1);
t1.Start();
Thread t2 = new Thread(thread2);
t2.Start();
}
static void thread1()
{
string newstring="123";
}
static void thread2()
{
//what to do here...what code will be here?
Console.WriteLine(newstring);
}
In thread1 can be whatever, but i need to get this "whatever", than i can use it in thread2
Data, which is used by both Thread must be commonly shared between both thread.
usually it is called common resource.
One this you must note that you have to achieve synchronization here.
As both threads are running independently and also reading/writing common data, chances of Race Condition is pretty high. To prevent such cases, you must implement synchronization on reading/writing data (on common object).
refere below code, where CommonResource is common between both threads and synchronization has been achieved by locking
In your example, one thread is writing data and other thread is reading data. If we don't implement Synchronization, there are chances that while thread 1 is writing new data, but thread 2 (because it is not waiting for thread 1 to complete it's task first) will bring old data (or invalid data).
Situation goes worst when there are multiple threads which are writing data, without waiting for other threads to complete their writing.
public class CommonResourceClass
{
object lockObj;
//Note: here main resource is private
//(thus not in scope of any thread)
string commonString;
//while prop is public where we have lock
public string CommonResource
{
get
{
lock (lockObj)
{
Console.WriteLine(DateTime.Now.ToString() + " $$$$$$$$$$$$$$$ Reading");
Thread.Sleep(1000 * 2);
return commonString;
}
}
set
{
lock (lockObj)
{
Console.WriteLine(DateTime.Now.ToString() + " ************* Writing");
Thread.Sleep(1000 * 5);
commonString = value;
}
}
}
public CommonResourceClass()
{
lockObj = new object();
}
}
and Thread calling be like
static CommonResourceClass commonResourceClass;
static void Main(string[] args)
{
commonResourceClass = new CommonResourceClass();
Thread t1 = new Thread(ThreadOneRunner);
Thread t2 = new Thread(ThreadTwoRunner);
t1.Start();
t2.Start();
}
static void ThreadOneRunner()
{
while(true)
{
Console.WriteLine(DateTime.Now.ToString() + " *******Trying To Write");
commonResourceClass.CommonResource = "Written";
Console.WriteLine(DateTime.Now.ToString() + " *******Writing Done");
}
}
static void ThreadTwoRunner()
{
while(true)
{
Console.WriteLine(DateTime.Now.ToString() + " $$$$$$$Trying To Read");
string Data = commonResourceClass.CommonResource;
Console.WriteLine(DateTime.Now.ToString() + " $$$$$$$Reading Done");
}
}
Output of it:
Note That, reading is taking 2 seconds and writing is taking 5 seconds, so reading is supposed to be faster. But if writing is going on, reading must wait till writing done.
you can clearly see in output, as one thread is trying to read or write, it cannot do it while other thread is performing it's task.

How can i use AutoResetEventHandler to signal Main thread function to start threads again once the first set of worker threads are done processing

Requirement :- At any given point of time only 4 threads should be calling four different functions. As soon as these threads complete, next available thread should call the same functions.
Current code :- This seems to be the worst possible way to achieve something like this. While(True) will cause unnecessary CPU spikes and i could see CPU rising to 70% when running the following code.
Question :- How can i use AutoResetEventHandler to signal Main thread Process() function to start next 4 threads again once the first 4 worker threads are done processing without wasting CPU cycles. Please suggest
public class Demo
{
object protect = new object();
private int counter;
public void Process()
{
int maxthread = 4;
while (true)
{
if (counter <= maxthread)
{
counter++;
Thread t = new Thread(new ThreadStart(DoSomething));
t.Start();
}
}
}
private void DoSomething()
{
try
{
Thread.Sleep(50000); //simulate long running process
}
finally
{
lock (protect)
{
counter--;
}
}
}
You can use TPL to achieve what you want in a simpler way. If you run the code below you'll notice that an entry is written after each thread terminates and only after all four threads terminate the "Finished batch" entry is written.
This sample uses the Task.WaitAll to wait for the completion of all tasks. The code uses an infinite loop for illustration purposes only, you should calculate the hasPendingWork condition based on your requirements so that you only start a new batch of tasks if required.
For example:
private static void Main(string[] args)
{
bool hasPendingWork = true;
do
{
var tasks = InitiateTasks();
Task.WaitAll(tasks);
Console.WriteLine("Finished batch...");
} while (hasPendingWork);
}
private static Task[] InitiateTasks()
{
var tasks = new Task[4];
for (int i = 0; i < tasks.Length; i++)
{
int wait = 1000*i;
tasks[i] = Task.Factory.StartNew(() =>
{
Thread.Sleep(wait);
Console.WriteLine("Finished waiting: {0}", wait);
});
}
return tasks;
}
One other thing, from the textual requirement section on your question I'm lead to believe that a batch of four new threads should only start after all previously four threads completed. However the code you posted is not compatible with that requirement, since it starts a new thread immediately after a previous thread terminate. You should clarify what exactly is your requirement.
UPDATE:
If you want to start a thread immediately after one of the four threads terminate you can still use TPL instead of starting new threads explicitly but you can limit the number of running threads to four by using a SemaphoreSlim. For example:
private static SemaphoreSlim TaskController = new SemaphoreSlim(4);
private static void Main(string[] args)
{
var random = new Random(570);
while (true)
{
// Blocks thread without wasting CPU
// if the number of resources (4) is exhausted
TaskController.Wait();
Task.Factory.StartNew(() =>
{
Console.WriteLine("Started");
Thread.Sleep(random.Next(1000, 3000));
Console.WriteLine("Completed");
// Releases a resource meaning TaskController.Wait will unblock
TaskController.Release();
});
}
}

How should i write producer /consumer code in C#?

I have 1 thread streaming data and a 2nd (the threadpool) processing the data. The data processing takes around 100ms so I use to second thread so not to hold up the 1st thread.
While the 2nd thread is processing the data the 1st thread adds the data to a dictionary cache then when the 2nd thread is finished it processes the cached values.
My questions is this how should be doing producer /consumer code in C#?
public delegate void OnValue(ulong value);
public class Runner
{
public event OnValue OnValueEvent;
private readonly IDictionary<string, ulong> _cache = new Dictionary<string, ulong>(StringComparer.InvariantCultureIgnoreCase);
private readonly AutoResetEvent _cachePublisherWaitHandle = new AutoResetEvent(true);
public void Start()
{
for (ulong i = 0; i < 500; i++)
{
DataStreamHandler(i.ToString(), i);
}
}
private void DataStreamHandler(string id, ulong value)
{
_cache[id] = value;
if (_cachePublisherWaitHandle.WaitOne(1))
{
IList<ulong> tempValues = new List<ulong>(_cache.Values);
_cache.Clear();
_cachePublisherWaitHandle.Reset();
ThreadPool.UnsafeQueueUserWorkItem(delegate
{
try
{
foreach (ulong value1 in tempValues)
if (OnValueEvent != null)
OnValueEvent(value1);
}
finally
{
_cachePublisherWaitHandle.Set();
}
}, null);
}
else
{
Console.WriteLine(string.Format("Buffered value: {0}.", value));
}
}
}
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
Runner r = new Runner();
r.OnValueEvent += delegate(ulong x)
{
Console.WriteLine(string.Format("Processed value: {0}.", x));
Thread.Sleep(100);
if(x == 499)
{
sw.Stop();
Console.WriteLine(string.Format("Time: {0}.", sw.ElapsedMilliseconds));
}
};
r.Start();
Console.WriteLine("Done");
Console.ReadLine();
}
}
The best practice for setting up the producer-consumer pattern is to use the BlockingCollection class which is available in .NET 4.0 or as a separate download of the Reactive Extensions framework. The idea is that the producers will enqueue using the Add method and the consumers will dequeue using the Take method which blocks if the queue is empty. Like SwDevMan81 pointed out the Albahari site has a really good writeup on how to make it work correctly if you want to go the manual route.
There is a good article on MSDN about Synchronizing the Producer and Consumer. There is also a good example on the Albahari site.

Categories

Resources