0
votes

Ok, I've a application which has two additional threads.

Thread one accesses object O and inserts data into a deque which is part of object O and increments a counter variable.

Thread two accesses object O and pulls out and deletes data from the deque and decrements the counter variable.

This turned out to give unexpected results since one thread tells me there are x elements inside of the deque and the other thread tells me there are no elements. I assume I have to use some kind of synchronization. I tried to use semaphore which I must have misunderstood since it didn't work (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx).

So I want to know how to access a global object from two threads. Be aware that the access to the global object O happens quite often since the access is within a while loop which results in continues insertions and polls. (Would the possible solution block the other thread from accessing the object and therefore the while loop?)

So far I only know of semaphore and mutex but have never used any of them, please be so kind and enlighten me.

2
Seems like you just described the producer-consumer problem.Jesse Good

2 Answers

1
votes

The easiest way would be to use EnterCriticalSection/LeaveCriticalSection around the code you want to use in an exclusive manner:

CRITICAL_SECTION critSect;

// Later in the code
EnterCriticalSection(&critSect);
// Do stuff with O
LeaveCriticalSection(&critSect);

You would of course use this in both threads. Only one thread can be inside the enter/leave zone at a time. The EnterCriticalSection will block until the other thread calls LeaveCriticalSection.

0
votes

When accessing an object from multiple threads with one access possibly changing the object, you need some from of synchronization. For the scenario described you probably want to have a queue class which does the necessary thread protection and signalling. Here is a simple implementation:

#include <mutex>
#include <condition_variable>
#include <deque>

template <typename T>
class queue
{
private:
    std::mutex              d_mutex;
    std::condition_variable d_condition;
    std::deque<T>           d_queue;
public:
    void push(T const& value) {
        {
            std::unique_lock<std::mutex> lock(this->d_mutex);
            d_queue.push_front(value);
        }
        this->d_condition.notify_one();
    }
    T pop() {
        std::unique_lock<std::mutex> lock(this->d_mutex);
        this->d_condition.wait(lock, [=]{ return !this->d_queue.empty(); });
        T rc(std::move(this->d_queue.back()));
        this->d_queue.pop_back();
        return rc;
    }
};

The code uses C++ 2011 constructs but it can easily be changed to avoid their use and rather use C++ 2003 constructs instead except that these aren't standardized.

The key points are:

  1. A std::mutex is used to make sure only on thread accesses the queue at a time.
  2. When putting something into the queue a lock on the mutex is acquired and the object is inserted into the queue. Once the object is inserted the lock is automatically released and a condition variable is signaled.
  3. When extracting something from the queue a lock on the mutex is acquired and the thread waits for the queue to be non-empty using a condition variable: if there is something in the queue already, the condition will be true and wait() returns immediately, otherwise the thread is put to sleep until it gets a signal at which point the condition is reevaluated. Note, that the condition may be evaluated multiple times because there may be spurious wake-up to the condition variable.
  4. The lambda captures its context by value: all it really captures is this; member variables cannot be captured directly because they are not part of the local context.
  5. The result from pop() is returned by value: the moment the lock gets release, the container may be changed, making it impossible to return objects by reference, even if they were put into a suitable location.

The main reason this is somewhat of a toy example is that it doesn't have a nice way to shut down the system: If a thread is blocked on the queue it waits until there is another object. A real implementation would have some way to signal that it is time to shut down, possibly throwing an exception from pop(). Also, sometimes it is useful to have queue which doesn't force blocking to extract an object. Instead, it would have a function try_pop() which acquires a lock, checks if the queue is non-empty and depending on the result extracts and object or signals failure. A function like this is easy to implement, though.