This question already has answers here:
.NET Reverse Semaphore?
(8 answers)
Closed 8 years ago.
Is there a way I can wait for more than one Release() in a Semaphore?
Say I have something like this:
class GoRunners {
Semaphore runnersSemaphore;
List<Result> results = new List<Result>();
private void RunForestRun(object aRunner) {
runnersSemaphore.Wait();
Runner runner = (Runner)aRunner;
results.Add(runner.Run());
runnersSemaphore.Release();
}
private List<Result> Go() {
List<Runners>() runners = CreateSomeRunners();
runnersSemaphore = new Semaphore(2, 2); // At most two threads at once
runners.ForEach((runner) => new Thread(RunForestRun).Start(runner); )}
runnersSemaphore.WaitFor(runners.Count); //How do I do this?
return results;
}
}
I know I can use multiple WaitOne()s inside the loop, but that just doesn't look good. But if there is no other way, I'm fine with it. If there is another mechanism that achieves what I want that's OK as well (I used to do stuff like this in Java using Semaphores, so my mind went in that direction).
Note: I'm locked in .NET 3.5 :(
You need to move your rate limiting code inside the foreach loop, that way the loop does not exit until after all of the runners have started. Once you have done that you only need to wait for the remaining two runners to finish before you return the result.
class GoRunners {
Semaphore runnersSemaphore;
List<Result> results = new List<Result>();
private void RunForestRun(object aRunner) {
try {
Runner runner = (Runner)aRunner;
var result = runner.Run();
lock(results)
{
results.Add(result)//List is not thread safe, you need to lock on it or use a different threadsafe collection (I don't know if there are any in .NET 3.5)
}
}
finally { //A little safety in case a execption gets thrown inside "runner.Run()"
runnersSemaphore.Release();
}
}
const int MAX_RUNNERS = 2; //I hate magic numbers in code if they get spread across more than one line, move the max out to a const variable.
private List<Result> Go() {
List<Runners>() runners = CreateSomeRunners();
runnersSemaphore = new Semaphore(MAX_RUNNERS, MAX_RUNNERS); // At most two threads at once
foreach(var runner in runners)
{
runnersSemaphore.WaitOne(); //This goes in here now. New threads will not be started unless there is less than 2 runners running.
new Thread(RunForestRun).Start(runner);
}
for(int i = 0; i < MAX_RUNNERS; i++) {
runnersSemaphore.WaitOne(); //Wait for the currently running runners to finish.
}
return results;
}
}
Related
This question already has answers here:
Semaphore halting my thread
(1 answer)
Semaphore is deadlocking by halting code running on own thread [duplicate]
(2 answers)
Semaphore - What is the use of initial count?
(10 answers)
Closed 2 years ago.
public class SampleData
{
private static readonly Semaphore pool = new Semaphore(0,1);
public string Data => getFromFile();
private static string getFromFile()
{
pool.WaitOne();
var data =
File.ReadAllText("somefilepath");
pool.Release();
return data;
}
}
In program.cs
var tasks = new List<Task<string>>();
for(int i=0; i<=5; i++)
{
tasks.Add(Task.Run<string>(()=>
new SampleData().Data));
}
Task.WaitAll(tasks.ToArray());
When I run this, it never completes the tasks. Can any one tell me what's the issue here?
Thanks.
If getFromFile() throw an exception, semaphore will never be released and Task.WaitAll will wait forever, you need to move pool.Release() to finally section. That might be the reason for indefinite waiting.
private static string getFromFile()
{
pool.WaitOne();
try {
var data = File.ReadAllText("somefilepath");
return data;
}
finally {
pool.Release();
}
}
}
First of all
You should consider using SemaphoreSlim if this is not cross process.
Secondly
In regards to your initialization of Semaphore(0,1). If initialCount is less than maximumCount, the effect is the same as if the current thread had called WaitOne (maximumCount minus initialCount) times.
That's to say, when you call pool.WaitOne(); in your current configuration, you are going to block until someone calls release (straight off the bat).
E.g.:
private static readonly Semaphore pool = new Semaphore(0,1);
static void Main(string[] args)
{
pool.WaitOne();
Console.WriteLine("This will never get executed");
}
It's likely you want:
Semaphore(1,1)
// or
SemaphoreSlim(1,1)
Lastly
You must always wrap these synchronization primitives in a try finally, or you will eventually deadlock if an exception gets thrown before Release.
SemaphoreSlim
await pool.WaitAsync();
try
{
// do something
}
finally
{
pool.Release();
}
Or if you really need to use a Semaphore
pool.WaitOne();
try
{
// do something
}
finally
{
pool.Release();
}
By using the below code firstly some of the calls are not getting made lets say out of 250 , 238 calls are made and rest doesn't.Secondly I am not sure if the calls are made at the rate of 20 calls per 10 seconds.
public List<ShowData> GetAllShowAndTheirCast()
{
ShowResponse allShows = GetAllShows();
ShowCasts showCast = new ShowCasts();
showCast.showCastList = new List<ShowData>();
using (Semaphore pool = new Semaphore(20, 20))
{
for (int i = 0; i < allShows.Shows.Length; i++)
{
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
{
showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
}));
pool.Release();
t.Start(i);
}
}
//for (int i = 0; i < allShows.Shows.Length; i++)
//{
// showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
//}
return showCast.showCastList;
}
public ShowData MapResponse(Show s)
{
CastResponse castres = new CastResponse();
castres.CastlistResponse = (GetShowCast(s.id)).CastlistResponse;
ShowData sd = new ShowData();
sd.id = s.id;
sd.name = s.name;
if (castres.CastlistResponse != null && castres.CastlistResponse.Any())
{
sd.cast = new List<CastData>();
foreach (var item in castres.CastlistResponse)
{
CastData cd = new CastData();
cd.birthday = item.person.birthday;
cd.id = item.person.id;
cd.name = item.person.name;
sd.cast.Add(cd);
}
}
return sd;
}
public ShowResponse GetAllShows()
{
ShowResponse response = new ShowResponse();
string showUrl = ClientAPIUtils.apiUrl + "shows";
response.Shows = JsonConvert.DeserializeObject<Show[]>(ClientAPIUtils.GetDataFromUrl(showUrl));
return response;
}
public CastResponse GetShowCast(int showid)
{
CastResponse res = new CastResponse();
string castUrl = ClientAPIUtils.apiUrl + "shows/" + showid + "/cast";
res.CastlistResponse = JsonConvert.DeserializeObject<List<Cast>>(ClientAPIUtils.GetDataFromUrl(castUrl));
return res;
}
All the Calls should be made , but I am not sure where they are getting aborted and even please let me know how to check the rate of calls being made.
I'm assuming that your goal is to process all data about shows but no more than 20 at once.
For that kind of task you should probably use ThreadPool and limit maximum number of concurrent threads using SetMaxThreads.
https://learn.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=netframework-4.7.2
You have to make sure that collection that you are using to store your results is thread-safe.
showCast.showCastList = new List<ShowData>();
I don't think that standard List is thread-safe. Thread-safe collection is ConcurrentBag (there are others as well). You can make standard list thread-safe but it requires more code. After you are done processing and need to have results in list or array you can convert collection to desired type.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=netframework-4.7.2
Now to usage of semaphore. What your semaphore is doing is ensuring that maximum 20 threads can be created at once. Assuming that this loop runs in your app main thread your semaphore has no purpose. To make it work you need to release semaphore once thread is completed; but you are calling thread Start() after calling Release(). That results in thread being executed outside "critical area".
using (Semaphore pool = new Semaphore(20, 20)) {
for (int i = 0; i < allShows.Shows.Length; i++) {
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
{
showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
pool.Release();
}));
t.Start(i);
}
}
I did not test this solution; additional problems might arise.
Another issue with this program is that it does not wait for all threads to complete. Once all threads are started; program will end. It is possible (and in your case I'm sure) that not all threads completed its operation; this is why ~240 data packets are done when program finishes.
thread.Join();
But if called right after Start() it will stop main thread until it is completed so to keep program concurrent you need to create a list of threads and Join() them at the end of program. It is not the best solution. How to wait on all threads that program can add to ThreadPool
Wait until all threads finished their work in ThreadPool
As final note you cannot access loop counter like that. Final value of loop counter is evaluated later and with test I ran; code has tendency to process odd records twice and skip even. This is happening because loop increases counter before previous thread is executed and causes to access elements outside bounds of array.
Possible solution to that is to create method that will create thread. Having it in separate method will evaluate allShows.Shows[i] to show before next loop pass.
public void CreateAndStartThread(Show show, Semaphore pool, ShowCasts showCast)
{
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((s) => {
showCast.showCastList.Add(MapResponse((Show)s));
pool.Release();
}));
t.Start(show);
}
Concurrent programming is tricky and I would highly recommend to do some exercises with examples on common pitfalls. Books on C# programming are sure to have a chapter or two on the topic. There are plenty of online courses and tutorials on this topic to learn from.
Edit:
Working solution. Still might have some issues.
public ShowCasts GetAllShowAndTheirCast()
{
ShowResponse allShows = GetAllShows();
ConcurrentBag<ShowData> result = new ConcurrentBag<ShowData>();
using (var countdownEvent = new CountdownEvent(allShows.Shows.Length))
{
using (Semaphore pool = new Semaphore(20, 20))
{
for (int i = 0; i < allShows.Shows.Length; i++)
{
CreateAndStartThread(allShows.Shows[i], pool, result, countdownEvent);
}
countdownEvent.Wait();
}
}
return new ShowCasts() { showCastList = result.ToList() };
}
public void CreateAndStartThread(Show show, Semaphore pool, ConcurrentBag<ShowData> result, CountdownEvent countdownEvent)
{
pool.WaitOne();
Thread t = new Thread(new ParameterizedThreadStart((s) =>
{
result.Add(MapResponse((Show)s));
pool.Release();
countdownEvent.Signal();
}));
t.Start(show);
}
I have a application, which to make concurrently task running. Here we set
MaxDegreeOfParallelism=4, which means at any time at most 4 tasks running concurrently. In this case, I only have 4 channels available. Otherwise an exception
Could not get Channel
will be thrown.
Each task will have an instance of OutboundDial, so at most it will be 4 instances.
public class OutboundDial
{
private ChannelResource m_ChannelResource;
private VoiceResource m_VoiceResource;
private TelephonyServer m_TelephonyServer;
private AppointmentReminderResult m_Result = new AppointmentReminderResult();
public OutboundDial(TelephonyServer telephonyServer)
{
m_TelephonyServer = telephonyServer;
}
internal void RunScript(AppointmentReminder callData)
{
try
{
try
{
m_ChannelResource = m_TelephonyServer.GetChannel();
m_VoiceResource = m_ChannelResource.VoiceResource;
}
catch (Exception ex)
{
Console.WriteLine("Could not get channel: {0}",ex.StackTrace);
return;
}
// a long running process of I/O bound operation
The producer-consumer queue is
public static BufferBlock<AppointmentReminder> m_Queue =
new BufferBlock<AppointmentReminder>(new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 4});
BufferBlock is a TPL class. The TelephontServer was initialized at the very beginning.
public static TelephonyServer ts;
ts = new TelephonyServer(sIpaddress, "username", "password");
In the consumer part, we have:
static async Task Consumer()
{
try
{
while (await m_Queue.OutputAvailableAsync())
{
m_Queue.TryReceive(4, ts); // MaxDegreeOfParallelism = 4
}
}
TryReceive is an extension method.
public static void TryReceive<T>(this BufferBlock<T> bufferBlock, int count, TelephonyServer ts) where T : AppointmentReminder
{
try
{
for (var i = 0; i < count; i++)
{
T item;
if (bufferBlock.TryReceive(out item))
{
Task t = Task.Run(() =>
{
OutboundDial d = new OutboundDial(ts);
d.RunScript<T>((T)item);
});
}
else
{
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
My question: I added 10 items to the queue in the producer part and I set a breaking point at the constructor.
I found the code run 10 times in the constructor then 10 times RunScript, which indicated 10 tasks run together rather than 4. But I only want 4(MaxDegreeOfParallelism). Therefore I did't have enough channels available, an exception was thrown.
Why in my extension method the concurrent running was not working?
BufferBlock doesn't really execute anything, so it doesn't make sense to specify its MaxDegreeOfParallelism. It works only because ExecutionDataflowBlockOptions inherits from DataflowBlockOptions, which is what the BufferBlock constructor expects.
Your Consumer() does this: take up to 4 items and execute them, take up to 4 items and execute them, take up to 4 items and execute them, etc. Since your never wait for those executions to complete, you're not actually limiting the degree of parallelism this way.
If you want to limit the degree of parallelism to 4, you could use ActionBlock, instead of your combination of BufferBlock and Consumer():
new ActionBlock<AppointmentReminder>(
reminder => new OutboundDial(ts).RunScript(reminder),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4});
What this does is to execute the lambda for each reminder, but at most 4 at the same time, which seems to be what you're asking for. Though I'm not sure it's what you need, since you don't seem to release (dispose) the channel after usage, to be used for the next reminder.
It is not clear where the problem is, but it seems that it is not that the cinstructor is called after the method.
Iwould advise you to change the constructor code to:
public OutboundDial(TelephonyServer telephonyServer)
{
m_TelephonyServer = telephonyServer;
Console.WriteLine(m_Telephonyserver);
}
And then you will see for sure that the constructor is complete.
Also, add some Console.WriteLine with useful informatino after each line in RunScript - then you will see where the error is coming from.
I'm looking for some advice on writing unit tests for multi-threading in C#. Specifically, I want to check that an object is being locked correctly. However, in order to test this I need to assert against that object, which may have changed before the assert(s) are implemented (with the lock being released, another thread may change the object).
Using AutoResetEvent I have been able to control the flow in the unit test side, allowing me to effectively emulate the lock in the tested object. The issue with this is that I no longer need the lock for the test to pass.
What I'd like is to have a test that passes with the lock in and fails with it out.
Obviously, this is a simplified example. It's also .Net 4, so there is no async and await option (although if that would help, changing could be an option).
Suggestions welcome. Thanks.
Below is example code:
public class BasicClass
{
public int Val
{
get { lock (lockingObject) { return val; } }
private set { lock (lockingObject) { val = value; } }
}
private int val;
public BasicClass(int val = -1)
{
Val = val;
}
public void SetValue(int val)
{
Val = val;
}
private object lockingObject = new object();
}
This is the (NUnit) unit test:
[Test]
public void BasicClassTest()
{
for (int repeat = 0; repeat < 1000; repeat++) // Purely for dev testing and can get away with as no SetUp/TearDown
{
BasicClass b = new BasicClass();
int taskCount = 10;
Task[] tasks = new Task[taskCount];
var taskControl = new AutoResetEvent(false);
var resultControl = new AutoResetEvent(false);
int expected = -1;
for (int i = 0; i < taskCount; i++)
{
int temp = i;
tasks[temp] = new Task(() =>
{
taskControl.WaitOne(); // Hold there here until set
b.SetValue(temp);
expected = temp;
resultControl.Set(); // Allows asserts to be processed.
});
}
// Start each task
foreach (var t in tasks)
t.Start();
// Assert results as tasks finish.
for (int i = 0; i < taskCount; i++)
{
taskControl.Set(); // Unblock, allow one thread to proceed.
resultControl.WaitOne(); // Wait for a task to set a expected value
Assert.That(b.Val, Is.EqualTo(expected));
Console.WriteLine("b.Val = {0}, expected = {1}", b.Val, expected); // Output values to ensure they are changing
}
// Wait for all tasks to finish, but not forever.
Task.WaitAll(tasks, 1000);
}
}
As for other system functions like DateTime.Now, I prefer to abstract threading functions like sleep, mutex, signals and so on (yes, I know there are libraries for DateTime.Now and other system functions, but I think to abstract it is a better way).
So you end up with a kind of IThreadind interface with methods to Sleep and so on. The disadvantage is, that you can't use the handy lock statement in this case. You could have a method Lock(object) that returns you an IDisposable that you can use with the "using" statement, to get nearly the same comfort.
using(threading.Lock(lockObject))
{
...
}
Now you can Create a real implementation with the real functions and a Mock for your unit tests which is injected. So you could for example for your tests shortcut any sleep call to e few ms in order to speed up your tests. And you can verify that all functions where called that you expected.
Sounds like a lot of work? Think over, how many time you will spend to debug some nasty threading issue which from time to time crashes your production system with your customer running amok.
I'm using Parallel.ForEach to work a bunch of items. The problem is, I want to prioritize which items get worked depending on the number of workers (slots) that are open. E.g. if I am working 8 parallel things and a slot opens between task 1-4, I want to assign easy work to those slots. The bottom half of the slots will get the hard work. This way, I won't get all 8 slots tied up doing hard/long-running work, easy/quick items will be run first. I've implemented this as follows:
The Code
const int workers = 8;
List<Thing> thingsToDo = ...; //Get the things that need to be done.
Thing[] currentlyWorkingThings = new Thing[workers]; //One slot for each worker.
void Run() {
Parallel.ForEach(PrioritizeThings(thingsToDo), o => {
int index = 0;
//"PrioritizeTasks" added this thing to the list of currentlyWorkingThings.
//Find my position in this list.
lock (currentlyWorkingThings)
index = currentlyWorkingThings.IndexOf(o);
//Do work on this thing...
//Then remove it from the list of currently working things, thereby
// opening a new slot when this worker returns/finishes.
lock (currentlyWorkingThings)
currentlyWorkingThings[index] = null;
});
}
IEnumerable<Thing> PrioritizeThings(List<Thing> thingsToDo) {
int slots = workers;
int halfSlots = (int)Math.Ceiling(slots / 2f);
//Sort thingsToDo by their difficulty, easiest first.
//Loop until we've worked every Thing.
while (thingsToDo.Count > 0) {
int slotToFill = ...; //Find the first open slot.
Thing nextThing = null;
lock (currentlyWorkingThings) {
//If the slot is in the "top half", get the next easy thing - otherwise
// get the next hard thing.
if (slotToFill < halfSlots)
nextThing = thingsToDo.First();
else
nextThing = thingsToDo.Last();
//Add the nextThing to the list of currentlyWorkingThings and remove it from
// the list of thingsToDo.
currentlyWorkingThings[slotToFill] = nextThing;
thingsToDo.Remove(nextThing);
}
//Return the nextThing to work.
yield return nextThing;
}
}
The Problem
So the issue I'm seeing here is that Parallel is requesting the next thing to work on from PrioritizeThings before a slot has opened (before an existing thing has been completed). I assume that Parallel is looking ahead and getting things to work ready in advance. I'd like it to not do this, and only fill a worker/slot when it is completely done. The only way I've thought of to fix this is to add a sleep/wait loop in PrioritizeThings which won't return a thing to work until it sees a legitimate open slot. But I don't like that and I was hoping that there was some way to make Parallel wait longer before getting work. Any suggestions?
There is a way built in (kinda) to support exactly the situation you are describing.
When you create the ForEach you will need to pass in a ParallelOptions with a non-standard TaskScheduler. The hard part is creating a TaskSchedueler to do that priority system for you, fortunately Microsoft released a pack of examples that contains one such scheduler called "ParallelExtensionsExtras" with its scheduler QueuedTaskScheduler
private static void Main(string[] args)
{
int totalMaxConcurrancy = Environment.ProcessorCount;
int highPriorityMaxConcurrancy = totalMaxConcurrancy / 2;
if (highPriorityMaxConcurrancy == 0)
highPriorityMaxConcurrancy = 1;
QueuedTaskScheduler qts = new QueuedTaskScheduler(TaskScheduler.Default, totalMaxConcurrancy);
var highPriortiyScheduler = qts.ActivateNewQueue(0);
var lowPriorityScheduler = qts.ActivateNewQueue(1);
BlockingCollection<Foo> highPriorityWork = new BlockingCollection<Foo>();
BlockingCollection<Foo> lowPriorityWork = new BlockingCollection<Foo>();
List<Task> processors = new List<Task>(2);
processors.Add(Task.Factory.StartNew(() =>
{
Parallel.ForEach(highPriorityWork.GetConsumingPartitioner(), //.GetConsumingPartitioner() is also from ParallelExtensionExtras, it gives better performance than .GetConsumingEnumerable() with Parallel.ForEeach(
new ParallelOptions() { TaskScheduler = highPriortiyScheduler, MaxDegreeOfParallelism = highPriorityMaxConcurrancy },
ProcessWork);
}, TaskCreationOptions.LongRunning));
processors.Add(Task.Factory.StartNew(() =>
{
Parallel.ForEach(lowPriorityWork.GetConsumingPartitioner(),
new ParallelOptions() { TaskScheduler = lowPriorityScheduler},
ProcessWork);
}, TaskCreationOptions.LongRunning));
//Add some work to do here to the highPriorityWork or lowPriorityWork collections
//Lets the blocking collections know we are no-longer going to be adding new items so it will break out of the `ForEach` once it has finished the pending work.
highPriorityWork.CompleteAdding();
lowPriorityWork.CompleteAdding();
//Waits for the two collections to compleatly empty before continueing
Task.WaitAll(processors.ToArray());
}
private static void ProcessWork(Foo work)
{
//...
}
Even though you have two instances of Parallel.ForEach running the combined total of both of them will not use more than the value you passed in for MaxConcurrency in to the QueuedTaskScheduler constructor and it will give preference to emptying the highPriorityWork collection first if there is work to do in both (up to a limit of 1/2 of all of the available slots so that you don't choke the low priority queue, you could easily adjust this to be a higher or lower ratio depending on your performance needs).
If you don't want the high priority to always win and you rather have a "round-robin" style scheduler that alternates between the two lists (so you don't want the quick items to always win, but just have them shuffled in with the slow items) you can set the same priority level to two or more queues (or just use the RoundRobinTaskSchedulerQueue which does the same thing)