Getting started with threads in D

Sean Kelly sean at invisibleduck.org
Fri Jun 22 12:17:02 PDT 2012


On Jun 22, 2012, at 11:17 AM, Henrik Valter Vogelius Hansson wrote:
> 
> Aight been reading a lot now about it. I'm interested in the TaskPool but there is a problem and also why I have to think about threads. OpenGL/DirectX contexts are only valid for one thread at a time. And with the task pool I can't control what thread to be used with the specified task right?

That's pretty much the entire point of a thread pool--it aims for optimal task completion time, and does this via an opaque scheduling mechanism.

> At least from what I could find I couldn't. So that's out of the question. The concurrency library is... I don't know. I most usually do a very fast synchronization swap(just swap two pointers) while the concurrency library seems like it would halt both threads for a longer time. Or am I viewing this from the wrong direction? Should I do it like lazy evaluation maybe? If you need code examples of what I am talking about I can give you that. Though I don't know the code-tag for this message board.

Games are an odd bird in that performance comes at the expense of much else, and that it really isn't easy to parallelize the main loop.  That said, the only time the concurrency library would halt a thread is if you do a receive() with no timeout and the message you want isn't in the queue.  So you can bypass this by using a timeout of 0 (basically a peek operation), and changing the code path based on whether the desired message was received.

> I will still use the task pool I think though all OpenGL calls will have to be routed so they are all done on the same thread somehow.

I think that will net you worse performance than if the main thread just did everything.  You still have synchronous execution but thread synchronization on top of that.  Can ownership of an OpenGL/DirectX contact be passed between threads?  Can you maybe just give every thread its own context and let it process whatever task you give to it, or is a context necessarily linked with some set of operations?

> The message box for the threads in concurrency, are they thread safe? Let's say we have two logic tasks running in parallel and both are sending messages to the graphics thread. Would that result in undefined behavior or does the concurrency library handle this kind of scenario for you?

Since it's a concurrency library, of course the API is thread safe :-)  Basically, how receive() works is it first looks in a thread-local queue for the desired message.  If one wasn't found it acquires a lock on that thread's shared message queue, moves the shared queue elements into the local queue, and releases the mutex.  Then it scans the new elements in the list for a match.  If it still doesn't find one, it re-acquires the mutex on the shared queue, and does the same thing.  If the shared queue is ever empty during this process, receive() will block on a condition variable up to the supplied timeout value.

The only performance issue with the concurrency API right now is that it allocates a struct to wrap each sent message, so there is some GC load.  I experimented with using a shared free list instead however, and it didn't really help performance in my test cases.  I suspect I'd either have to go to a lock-free free list, or something other fairly fancy approach.  Beyond that, I've experimented with using ref and not using ref attributes for parameters everywhere applicable, etc.  The current implementation is as fast as I could get things.

For future directions, I really want to add inter-process messaging.  That means serialization support and a scalable socket implementation though.  Not to mention free time.  I've considered just hacking together the implementation and limiting inter-process messages to concrete variables as a proof of concept.  That would need just free time.


More information about the Digitalmars-d-learn mailing list