views:

221

answers:

1

I'm preparing for an interview in a few weeks and I thougth I would give threads in boost a go, as well as do the simple producer/consumer problem I learned in school.

Haven't done it quite awhile so I was curious what you guys think of this? What should I add to make it a better example etc. Thanks for the feedback! :)

//////////////////////////////////////////////////////////////////////////
boost::mutex bufferMutex;
deque<int> buffer;
const int maxBufferSize = 5;
//////////////////////////////////////////////////////////////////////////

bool AddToBuffer(int i)
{
    if (buffer.size() < maxBufferSize)
    {
        buffer.push_back(i);
        return true;
    }
    else
    {       
        return false;
    }
}

bool GetFromBuffer(int& toReturn)
{
    if (buffer.size() == 0)
    {
        return false;
    }
    else
    {
        toReturn = buffer[buffer.size()-1];
        buffer.pop_back();
        return true;
    }
}

struct Producer 
{
    int ID;
    void operator()()
    {
        while (true)
        {
            boost::mutex::scoped_lock lock(bufferMutex);
            int num = dice();
            bool result = AddToBuffer(num);
            lock.unlock();
            //safe area done
            if (result)
            {
                cout << "Producer " << this->ID << " Added " << num << endl;
            }
            else
            {
                cout << "!!Buffer was Full!!" << endl;
            }
            //Added
            //Now wait
            boost::xtime xt;
            xtime_get( &xt, boost::TIME_UTC);
            xt.nsec += 1000000 + 100000 * (rand() % 1000);
            boost::thread::sleep(xt);
        }
    }
};

struct Consumer 
{
    int ID;
    void operator()()
    {
        while (true)
        {
            int returnedInt = 0;
            boost::mutex::scoped_lock lock(bufferMutex);
            bool result = GetFromBuffer(returnedInt);
            lock.unlock();
            //safe area done
            if (result)
            {
                cout << "\tConsumer " << this->ID << " Took Out " << returnedInt << endl;
            }
            else
            {
                cout << "!!Buffer was Empty!!" << endl;
            }
            //Added
            //Now wait
            boost::xtime xt;
            xtime_get( &xt, boost::TIME_UTC);
            xt.nsec += 1000000 + 100000 * (rand() % 1000);
            boost::thread::sleep(xt);
        }
    }
};



void main()
{
    Producer p, p2;
    Consumer c, c2;

    p.ID = 1;
    p2.ID = 2;

    c.ID = 1;
    c2.ID = 2;

    boost::thread thread1(boost::ref(p));
    boost::thread thread2(boost::ref(c));
    boost::thread thread3(boost::ref(p2));
    boost::thread thread4(boost::ref(c2));

    int x;
    cin >> x;
}
+2  A: 

If you are already wrapping your buffer object in calls like AddToBuffer and GetFromBuffer, it would make more sense for you to put the locking within your wrapper functions. In addition, you are making an explicit call to unlock, which completely defeats the purpose of scoped_lock; scoped_lock uses Resource Acquisition is Initialization (RAII) to both acquire and release the lock. A better use would be to place the critical section within a block so that the mutex is released due to the lock going out of scope rather than due to an explicit call to the unlock function, as scopes are less fragile. For example:

// Code that doesn't need locking
{
    boost::mutex::scoped_lock lck(bufferMutex); // Lock is acquired here
    // Code that needs to be synchronized
} // Lock is automatically released here without explicit call to unlock()
// More code that doesn't need locking
Michael Aaron Safyan
Thanks for the comment! :)
bobber205