C# - concurrent queue in .NET 2.0
-
[pre-blakeyrant counter-argument] Here's my options:
So I'm stuck with .NET 2.0. And that doesn't include the stuff that Microsoft stole from Go for version 4.0.
I currently have some code that does this:
(pseudo-code) start "n" threads: loop forever: lock "queue": is "queue" empty? yes -> sleep 10ms, restart loop no -> remove element from "queue", store in "req" process "req" into "res" lock "result_queue": add "res" to "result_queue"
And as you know, sleep in concurrent code is WRONG BAD BAD WRONG SPANK COMPLAIN
Here's an example in Go of a program that does what I want:
Is this even possible in .NET 2.0?
-
I believe .NET 2.0 has everything you need to implement
BlockingCollection<T>
(which BTW is what you want.) The suck is that you have to write it yourself, since it didn't ship with the framework as of 2.x.Basically the idea is to create an iterator over a collection that "blocks" a thread which requests a new collection member if no new collection member is available. And throws an exception if the cancel flag is set. (Since there's no other way to "communicate" cancellation to a blocked thread.)
Here's the source:
-
StealImport/Implement missing parts as needed.
-
Sounds like you need to learn about semaphores.
-
Are AutoResetEvent and ManualResetEvent available in .NET 2.0?
-
I've used semaphores, but I haven't used C# all that much.
-
Are AutoResetEvent and ManualResetEvent available in .NET 2.0?
Yes, and that was the first thing I thought of when I read what he was trying to do.
-
I remember rolling my own simple blocking queue with an internal
List<T>
that calls aMonitor.Pulse()
after an element was added and theDequeue()
method has a correspondingMonitor.Wait()
.
-
And as you know, sleep in concurrent code is WRONG BAD BAD WRONG SPANK COMPLAIN
Why is sleep bad in concurrent code? Other than potentially wasting 10ms I guess.
-
10ms is nearly a whole frame if you're aiming for 60fps. There are basically 4 cases:
- Worker thread has work, continues to have work
This works the same way no matter what - the thread processes the work continuously. - Worker thread does not have work, continues to not have work
The pseudo-code in the OP would wake up the thread every 10ms to check if there was work, even if it never got work. A blocking queue would not wake up the thread until there was something in the queue. - Worker thread has work, finishes all work
The worker thread blocks until there's more work. Or just continuously polls until there's more work. - Worker thread does not have work, gets work
With a blocking queue, the moment the thread is running on the CPU it will start processing the work. With polling, it can take up to 10ms for the thread to even realize there's work.
Sleeping in concurrent code instead of using synchronization primitives is almost always a sign of incorrect understanding of how concurrency works. For example, a web server doesn't wake up every n seconds to see if it should serve each page. It waits until there is a request for a page and then immediately starts working on the page.
To be clear, this isn't my original code:
- Worker thread has work, continues to have work