Threading question

Started by
15 comments, last by Antheus 13 years, 8 months ago
I'm new to multithreaded programming, and I'm trying to implement resource loading on another thread, but I'm not sure how to get this working. What I have so far is this (pseudocode):
void loadingThreadMain() {    while (true) {        // wait until alerted        condition.wait();        // lock, and copy into a temporary queue        queue temp_queue;        lock_resource_queue();        temp_queue.push( resource_queue.pop() );        unlock_resource_queue();        for each (i in temp_queue)            load( i );    }}// and in the main thread:void loadResource() {    // lock/add the resource    lock_resource_queue();    resource_queue.push( resource );    unlock_resource_queue();    // notify the other thread    condition.notify();}

However, suppose the ResourceThread is past the point of copying to the temp queue and loadResource() is called. A resource will be safely pushed onto the resource_queue, but condition.notify() will have no effect because the resource loading queue has not yet gotten to condition.wait(). This would be fixed if I constantly check (using a lock) in the resource thread if the resource_queue has anything, but this seems inefficient and also requires a ton of locking (basically constant locking/unlocking). What am I missing here?
Advertisement
With that type of design, you could use a semaphore for condition. When you add something to resource_queue you would increment the semaphore (usually called post or release, depending on the semaphore implementation) once for each item added, the loading thread would then block until at least one resource was waiting, and process one at a time.

void loadingThreadMain(){    while (true)     {        // wait until alerted        condition.wait(); // Wait until we can remove 1 from semaphore count        // lock, and copy        lock_resource_queue();        resource = resource_queue.pop();        unlock_resource_queue();        load(resource);    }}// and in the main thread:void loadResource(){    // lock/add the resource    lock_resource_queue();    resource_queue.push( resource );    unlock_resource_queue();    // notify the other thread    condition.post(); // Add 1 to the semaphore count}


The loading thread is processing resources serially, so you only pop one at a time and don't need a temp_queue. This also makes counting on the semaphore simpler and could let you have multiple loading threads processing the queue simultaneously.
Shoot Pixels Not People
Ah, that makes sense, I'll give it a shot. Thanks!

One more thing... I'm using boost for threading, and all I can find is boost::mutex. What is the name of boost's semaphore and critical section objects?
Boost's mutex is the equivalent of a Win32 "critical section". Unfortunately, Win32 chose to use the name 'mutex' for something else.

Boost threading doesn't have semaphores for some reason :(


Your original design should work with a bit of modification. condition.wait should actually be passed the lock - while it's waiting the lock is released (so the main thread can grab it, and update the condition variable), then once the worker-thread is resumed/notified, the lock is aquired again. Also, instead of just waiting on this condition, you can make a boolean variable that works alongside it.
volatile bool requireLoading = false;void loadingThreadMain() {    while (true) {        queue temp_queue;        {            boost::unique_lock<boost::mutex> lock(resourceMutex);            while(!requireLoading)            {                //this unlocks the mutex while it's waiting...                resourceCondition.wait(lock);                //...and then locks it again when it's notified            }            // copy into a temporary queue            assert( requireLoading );            requireLoading = false;            while( !resource_queue.empty() )                temp_queue.push( resource_queue.pop() );        }//unlock mutex        for each (i in temp_queue)            load( i );    }}// and in the main thread:void loadResource() {    {        boost::unique_lock<boost::mutex> lock(resourceMutex);        resource_queue.push( resource );        requireLoading = true;//notify the other thread    }//unlock mutex    resourceCondition.notify();//resume the other thread}


[Edited by - Hodgman on July 29, 2010 7:03:04 PM]
I'd just like to call attention to the fact that Hodgman tests the requireLoading flag in a loop around the wait on the condition variable.

You need to do this rather than using a simple if-test because while waiting on a condition variable, it may wake spuriously.
Thanks for the responses... one more question.

If getData() is called before the data is loaded, I want the main thread to block until it is. Currently what I have is:
// while the data has not been loaded/generated...while (!generated) {	// if it is due to a loading error, return NULL	if (fail)		return NULL;	// process the generate queue, which must be called on the main thread	// this function is thread safe, so once data	// has been added to the queue, processGenUngenQueue()	// will generate the data and set generated to true (or failed)	resourceLoaderManager.processGenUngenQueue();}return fail ? NULL : &resData

Of course, the problem with this is that the main thread constantly checks, rather than waiting until some notification.

Is there some way to pause and automatically resume once resourceCondition starts waiting? So what would happen is that while the loading thread did its thing, the main thread would wait until the loading thread hit resourceCondition.wait(), and it would call the generate function then. Like if there was some form of join() which blocked until a condition was hit, rather than until the thread ended.
If you're dealing with low level primitives, the way to wait for something is almost always to use a condition variable (or a semaphore, but you can transform code using semaphores to code using condition variables and vice versa, personally I find condition variables much easier to reason about).

But if you're planning to use threads a lot, it's worth moving a step higher up the abstraction ladder so you don't necessarily have to worry about locks, flags, condition variables and so on. If you can restructure things in terms of tasks rather than threads, it will make life much easier (anecdotally speaking). A concept that might be useful for you in this situation is that of a future -- an object that represents a value that may not yet have materialized.

If this was me, I wouldn't have any given thread dedicated to a particular kind of job (unless it was needed because a particular API demanded it). So, rather than having a "loading thread", I'd have a system where I could submit "loading tasks" to a thread pool, processing queue, or whatever:

// pseudocodetask_pool tasks;for (f in files_to_load)    tasks.add(bind(load_resource, f))// submit the tasks for processingfuture<void> done = tasks.flush(thread_pool);// perhaps do some other stuff in the mean time// ...done.wait(); // wait for resources to load.


I'd like to give a more targeted response, but I'm not sure what this getData() function you speak of is supposed to do or how it fits in with the rest of the system, as this is the first time you've mentioned it.

I have read about thread pools, and they do seem like a good scalable approach, but since I'm just using multithreading for this one thing (loading resources... this is just a hobbyist project and I'm already pretty far in, next time I'll start with a multithreaded design), I'd rather just stick with this.

Basically, once resources are loaded, the resource's data can be accessed using the getData() method. There is also an isReady() method. What I'd like to happen is if the user wants to allow the data to load in the background while the game is running, they should check first using the isReady() method. However, in situations where the player has moved, say, to quickly and all the data has not yet loaded, getData() should block until the resource either loaded, or failed to load (again, there are probably better designs, but this is fine for me).
Quote:Original post by Gumgo
Basically, once resources are loaded, the resource's data can be accessed using the getData() method. There is also an isReady() method. What I'd like to happen is if the user wants to allow the data to load in the background while the game is running, they should check first using the isReady() method. However, in situations where the player has moved, say, to quickly and all the data has not yet loaded, getData() should block until the resource either loaded, or failed to load (again, there are probably better designs, but this is fine for me).


The easiest thing would probably be to use the future/promise stuff in boost.

EDIT: I should probably add a little more meat to this response.

I think the easiest thing to do would be to have an object that maintains your resource loading thread. It might look something like this:

class ResourceLoader : noncopyable{    public:        ResourceLoader(); // starts loading_thread        ~ResourceLoader(); // stops the thread        shared_future<Resource> load(const std::string &resourceIdentity);        // ...    private:        void thread_body();        Resource load_synchronously(const std::string &resourceIdentity);    private:        thread loading_thread;        mutex mtx;        condition cond;        ResourceQueue resource_queue;        bool destructor_wants_to_stop;};


Here, ResourceQueue is actually a queue of boost::packaged_tasks. The functor associated with each packaged task is created by something like boost::bind(&ResourceLoader::load_synchronously, this, resourceIdentity). The load() method would add such a packaged tasks to the queue and return a corresponding future<Resource>.

The thread_body() method would be what the thread runs. It simply pulls packaged_tasks out of the queue and executes them.

You'll use the existing technique based on a condition variable to handle the queue synchronisation.

[Edited by - the_edd on July 31, 2010 6:51:53 PM]
Quote:Original post by Gumgo
getData() should block until the resource either loaded, or failed to load (again, there are probably better designs, but this is fine for me).


Blocking is crappy. Human brain can quickly accustom to any amount of latency (removing the latency results in simulator sindrome), but it gets hammered by jitter.

For smooth transitions, use proxy objects. Have a set of permanent proxies (texture, one bind-pose mesh for each distinct model). When starting to load a resource, start displaying it immediately using proxy resource. When it's loaded, blend, or even pop the real one.

Optionally, for meshes and such, displaying nothing is a better option than blocking, as long as invisible objects are loaded sufficiently in advance.

This approach also simplifies many things, since it avoids not_loaded special case. Consider dependencies - loading a model requires animation, mesh, textures, and perhaps even more. With above, you could get into staggered blocking. With proxies, each of those is rendered instantly, and then progressively refined. Model will be first in bind pose, then textures will be filled in, finally all the bumps, specular and other details.

It seems to be also the defacto technique for a very long time now.

This topic is closed to new replies.

Advertisement