A netstandard2.0 library that provides utility classes to help simplify some aspects of multi-threading and synchronization.
Read the API documentation carefully.
TaskScheduler
extensionRun
to mimic theTask.Run
API, ensuring tasks are run with the desired scheduler.IdleTaskScheduler
, a custom task scheduler that schedules tasks in a round robin manner with idle threads.ThreadWrapper
class to simplify the process of making a pauseable thread.ItemProcessor<TItem>
class encapsulating a queue+worker thread.- Various
Lock
extension methods to simplify using synchronization primitives ( e.g. SemaphoreSlimExtensions) - Various synchronized value holder generics to automatically use and release a specific locking mechanism when getting or setting a value. (e.g. ReaderWriterLockSlimValue)
Execute TaskSchedulerExtensions.Run
on a default instance of IdleTaskScheduler configured to provide 4 STA threads.
using Jcd.Threading;
using Jcd.Threading.Tasks;
var its = new IdleTaskScheduler(4,ApartmentState.STA,"My Scheduler");
its.Run(()=>{/* do some work.*/});
Use SemaphoreSlimExtensions.Lock
to automatically call Wait
and Release
using Jcd.Threading;
var sem = new SemaphoreSlim(1,1);
using (sem.Lock()) // This calls sem.Wait
{
// This is your critical section.
// Do only what needs to be done
// while the lock is held. Do everything
// else before or after.
}
// once .Dispose is called, sem.Release() is called.
Use ReaderWriterLockSlimValue
to synchronize reads and writes to a single
shared value. Useful for needing exclusive write access for multiple readers
whose use case can tolerate slightly stale data, such as thread specific status
updates.
using Jcd.Threading.SynchronizedValues;
const int initialValue=20;
var rwlsv = new ReaderWriterLockSlimValue<int>(initialValue);
// writer thread.
var wt = Task.Run(()=>{
var sw = StopWatch.StartNew();
int i=0;
do
{
i++;
rwlsv.Value = i; // here we communicate thread-local information blocking all reads as its written.
Thread.Sleep(100); // wait 0.1 seconds
}
while(sw.ElapsedMilliseconds < 1000)
});
// reader thread 1.
var rt1 = Task.Run(()=>{
var sw=StopWatch.StartNew();
do
{
// here we read the data maintained by the writer thread, blocking writes as its read.
Console.WriteLine($"[1] The count is: {rwlsv.Value}");
Thread.Sleep(71);
}
while(sw.ElapsedMilliseconds < 750)
});
// reader thread 2.
var rt1 = Task.Run(()=>{
var sw=StopWatch.StartNew();
do
{
// here we read the data maintained by the writer thread, blocking writes as its read.
Console.WriteLine($"[2] The count is: {rwlsv.Value}");
Thread.Sleep(50);
}
while(sw.ElapsedMilliseconds < 1500)
});
// wait for all threads to finish.
await Task.WhenAll(new[]{wt,rt1,rt2});
These were just an overview of what's available. See EXAMPLES.md for more and detailed examples.
And as always, read the API documentation