consumer/producer in c++

前端 未结 4 1686
灰色年华
灰色年华 2020-12-15 14:18

This is a classic c/p problem where some threads produce data while other read the data. Both the producer and consumers are sharing a const sized buffer. If the buffer is e

相关标签:
4条回答
  • 2020-12-15 14:44

    What I would do is this:

    • Make a data class that hides your queue
    • Create thread-safe accessor methods for saving a piece of data to the q, and removing a piece of data from the q ( I would use a single mutex, or a critical section for accessors)
    • Handle the case where a consumor doesn't have any data to work with (sleep)
    • Handle the case where the q is becoming too full, and the producers need to slow down
    • Let the threads go willy-nilly adding and removing as they produce / consume

    Also, remember to add a sleep into each and every thread, or else you'll peg the CPU and not give the thread scheduler a good spot to switch contexts and share the CPU with other threads / processes. You don't need to, but it's a good practice.

    0 讨论(0)
  • 2020-12-15 14:46

    Protect the queue accesses with a mutex, that should be it. A 'Computer Science 101' bounded producer-consumer queue needs two semaphores, (to manage the free/empty count and for producers/consumers to wait on, as you are already doing), and one mutex/futex/criticalSection to protect the queue.

    I don't see how replacing the semaphores and mutex with condvars is any great help. What's the point? How do you implement a bounded producer-consumer queue with condvars that works on all platforms with multiple producers/consumers?

    0 讨论(0)
  • 2020-12-15 14:51

    Your queue is not synchronized, so multiple producers could call push_back at the same time, or at the same time the consumer is calling pop_front ... this will break.

    The simple approach to making this work is to use a thread-safe queue, which can be a wrapper around the std::queue you already have, plus a mutex.

    You can start by adding a mutex, and locking/unlocking it around each call you forward to std::queue - for a single consumer that should be sufficient, for multiple consumers you'd need to fuse front() and pop_front() into a single synchronized call.

    To let the consumer block while the queue is empty, you can add a condition variable to your wrapper.

    That should be enough that you can find the answer online - sample code below.


    template <typename T> class SynchronizedQueue
    {
        std::queue<T> queue_;
        std::mutex mutex_;
        std::condition_variable condvar_;
    
        typedef std::lock_guard<std::mutex> lock;
        typedef std::unique_lock<std::mutex> ulock;
    
    public:
        void push(T const &val)
        {
            lock l(mutex_); // prevents multiple pushes corrupting queue_
            bool wake = queue_.empty(); // we may need to wake consumer
            queue_.push(val);
            if (wake) condvar_.notify_one();
        }
    
        T pop()
        {
            ulock u(mutex_);
            while (queue_.empty())
                condvar_.wait(u);
            // now queue_ is non-empty and we still have the lock
            T retval = queue_.front();
            queue_.pop();
            return retval;
        }
    };
    

    Replace std::mutex et al with whatever primitives your "Thread.h" gives you.

    0 讨论(0)
  • 2020-12-15 15:05

    When managing shared state like this, you need a condition variable and a mutex. The basic pattern is a function along the lines of:

    ScopedLock l( theMutex );
    while ( !conditionMet ) {
        theCondition.wait( theMutex );
    }
    doWhatever();
    theCondition.notify();
    

    In your case, I'd probably make the condition variable and the mutex members of the class implementing the queue. To write, the conditionMet would be !queue.full(), so you'd end up with something like:

    ScopedLock l( queue.myMutex );
    while ( queue.full() ) {
        queue.myCondition.wait();
    }
    queue.insert( whatever );
    queue.myCondition.notify();
    

    and to read:

    ScopedLock l( queue.myMutex );
    while ( queue.empty() ) {
        queue.myCondition.wait();
    }
    results = queue.extract();
    queue.myCondition.notify();
    return results;
    

    Depending on the threading interface, there may be two notify functions: notify one (which wakes up a single thread), and notify all (which wakes up all of the waiting threads); in this case, you'll need notify all (or you'll need two condition variables, one for space to write, and one for something to read, with each function waiting on one, but notifying the other).

    0 讨论(0)
提交回复
热议问题