Joel Graff
Joel Graff

Reputation: 239

using a vector in a muti-threaded application

I've been puzzling over a problem with a multi-threaded application I'm writing. Briefly, the main thread starts a hotloading thread that takes a list (vector) of file objects, checks their timestamps, and passes changed files to another vector.

The problem occurs when I attempt to add new files to the list of files that need to be checked. The hotloading thread is continually checking the files in its list for changes, locking / unlocking a mutex with a scoped lock as it iterates. Similarly, when the main thread calls the addItems() function, the same mutex is similarly locked, and new files are added.

The information I've read says this should be fine given the proper locking, and it does work, but I experience a very large slowdown as a result. That is, if I add even one file to the list, the program continuously slows, as if the threads are becoming increasingly locked...

The code:

void MyThread::addItems( ItemList newItems )
{

   ScopedLock<Mutex> lock( itemMutex_ );


    for ( ItemList::iterator it = newItems.begin(); it != newItems.end(); ++it )
    {
        if ( ... test condition on (*it) ... )
            items_.push_back( (*it) );
    }
};

void MyThread::run()
{
    _done = false;
    do
    {
        YieldCurrentThread();

        if ( !isEmpty_ )
        {
            ScopedLock<Mutex> lock( itemMutex_ );
            for ( ItemList::iterator it = items_.begin(); it != items_.end(); ++it )
            {
                if ( ... test condition on (*it) ... )
                    updatedItems_.push_back( (*it) );
            }
         }
    }
}

The run() function, of course, runs continuously, iterating over my items_ vector, with the addItems() function called from another thread to insert items into the vector. Only one thread calls addItems().

It can't be difficult, but I'm not seeing anything out there that hints at a solution I haven't already tried...

EDIT:

  1. A comment below reminded me to point out that if I do not call addItems(), the run() function does not appear to have any significant impact on application performance, wasteful as it may be.

Upvotes: 1

Views: 117

Answers (2)

John Calsbeek
John Calsbeek

Reputation: 36497

Your lock will spend very little time unlocked. Depending on the way YieldCurrentThread is implemented, I would expect the delay to be very brief, if indeed there is any delay at all. And the more things you have in your list, the longer on average that addItems will need to wait before it adds data to your vector. If your main thread is spending a lot of time calling addItems, I would definitely expect some slowdown.

One thing you can attempt is a thread loop that looks like this:

for (;;)
{
    ItemList snapshot;
    if ( !isEmpty_ )
    {
        ScopedLock<Mutex> lock( itemMutex_ );
        snapshot = items_;
    }
    ItemList updated;
    for ( ItemList::iterator it = snapshot.begin(); it != snapshot.end(); ++it )
    {
        if ( ... test condition on (*it) ... )
            updated.push_back( (*it) );
    }
    if ( !updated.empty() )
    {
        ScopedLock<Mutex> lock( itemMutex_ );
        updatedList_ = updated;
    }
}

The idea here is to take the heavy processing and move it out from under the mutex. Of course, this will only actually speed anything up if your actual bottleneck is lock contention. If the problem is just that more files take more time to check, then it's unrelated to the multiple threads.

Upvotes: 1

John Zwinck
John Zwinck

Reputation: 249293

What platform are you on? If you are trying to watch for changed files, there are operating system facilities to do that efficiently for you. On Linux that would be inotify. If you're on Windows, this looks like a good article: http://qualapps.blogspot.co.uk/2010/05/understanding-readdirectorychangesw.html

Upvotes: 2

Related Questions