1

Problem Statement :

I have a producer consumer thread sharing a shared data which is nothing but a structure. Consumer thread is waiting on a std::condition variable wait. the producer thread recvs packet writes down shared struture and notify the consumer thread.

challenges : the consumer thread has to process and send response with few milliseconds (say 10-15ms).

Issue - over a period of time , there are few ocassions where the thread itself taking time to wake up from the cv wait and thus not able to response in few ms.

i tried using yield , but that is hogging the cpu , and the requirement is that cpu utilization should be kept as minimum as possible.

I am interested in knowing : 1 - Why is thread taking so much time to wake up ..?? (more than 40 ms in some cases). 2 - Is there any other way to share data between threads without allowing thread to sleep and also utilizing minimum cpu.(tried pipe but givin less performance over current shared memory implementation).

3 - suggest any other design which may not include threads still keeping concerns seperately and acheiving the required latency .. ????

This is the shared data :

class Message
{
    typedef std::basic_string<uint8_t> MesgBuffType;
    MesgBuffType _buffer;

    static uint16_t _data[256];
  public :
    append(data .....,len ) append methods
}

void ProcessData(Messege msg)
{
    std::unique_lock<std::mutex> lock(mutex);
    sharedData.set(msg);

    lock.unlock();
    _syncCondVar.notify_one();
}

void consumeData()
{
    std::unique_lock<std::mutex> lock(mutex);
    _syncCondVar.wait(lock);
    Messege req;
    if (sharedData.get(req))
    {
        lock.unlock();
        processRequest(req);
    }
}

Added supporting Code.

VikramChopde
  • 143
  • 1
  • 11
  • Please, do not ever forget to specify the operating system and the compiler you are using, – Ilya Popov Jul 29 '15 at 12:34
  • Environment is Windows with VS2013 . – VikramChopde Jul 29 '15 at 12:46
  • Did you try moving `lock.unlock` after `notify_one`? – Ilya Popov Jul 29 '15 at 12:48
  • Can unlocking before the notify cause such issues ,,..?? coz i used it to eliminate the possibility of thread wake up after notify still not able to get the mutex as its already locked ..? – VikramChopde Jul 29 '15 at 12:50
  • See the second link in the @simon answer. It suggests that holding a lock while notifying can improve "predictability", whatever it means. I think it is at least worth trying. – Ilya Popov Jul 29 '15 at 12:52
  • And you have to wrap `wait` call in the while loop or use 2-argument version. There can be spurious wakeups. See the same link and [cppreference](http://en.cppreference.com/w/cpp/thread/condition_variable/wait) – Ilya Popov Jul 29 '15 at 12:56

3 Answers3

3

1 - Why is thread taking so much time to wake up ..?? (more than 40 ms in some cases).

It's impossible to say without seeing the code. Maybe you aren't releasing the mutex soon enough in the producer thread, or maybe the structure is on a cache line that the producer thread keeps writing to and the consumer has to repeatedly re-read the cache line, or maybe something else entirely. Your question should be closed for lack of information.

2 - Is there any other way to share data between threads without allowing thread to sleep and also utilizing minimum cpu.(tried pipe but givin less performance over current shared memory implementation).

Use atomic or lock-free structures, but they're hard to use correctly.

3 - suggest any other design which may not include threads still keeping concerns seperately and acheiving the required latency .. ????

Single-threaded, event-based models often have lower-latency than multithreaded models, largely because there is no context switching between threads and no need to synchronise access to data.

Jonathan Wakely
  • 153,269
  • 21
  • 303
  • 482
  • Jon - mutex is being released just before the notify call. we moved to multiple thread because in single thread the thread was busy processing the packet while a new packet was waiting to be recved . which caused more delay in processing that packet. R u sugggesting some kind of asynch io mechanism like iocompleation ports etc.? – VikramChopde Jul 29 '15 at 11:29
1

The code does not seem to use the condition variable correctly: you wait unconditionally on the condition variable even if new data is available. This is why you observe long wait times.

Condition variables are stateless: they do not remember missed notifications. You can also get spurious wake-ups. This is why some state must be associated with condition variables and waiting must be done in a loop, see std::condition_variable::wait for more details.

You may like to associate an update sequence counter with your message updates to fix this issue, e.g.:

Message global_message;
unsigned global_message_update = 0;

void ProcessData(Message const& msg)
{
    std::unique_lock<std::mutex> lock(mutex);

    global_message = msg;
    ++global_message_update;

    _syncCondVar.notify_one();
}

void consumeData()
{
    unsigned message_update = 0;
    Messege req;

    {
        std::unique_lock<std::mutex> lock(mutex);
        while(message_update == global_message_update)
            _syncCondVar.wait(lock);
        message_update = global_message_update;
        req = global_message;
    }

    // now process req
}
Maxim Egorushkin
  • 119,842
  • 14
  • 147
  • 239
  • Maxim - Wait time is not the issue , if data is not available the thread is bound to wait on cv , thw moment we get the data we notify so tht thread wakes up and process the data ... it is in some cases this wake up taking time . Processdata is getting called only when we recv the complete packet. – VikramChopde Jul 29 '15 at 15:05
  • k.. got ur comment .. u mean even if data is available we ll make the thread to sleep.. and wait for notification .. instead of directly getiing the data and processing it. – VikramChopde Jul 29 '15 at 15:47
  • @VikramChopde Yep, you got it right. There are also spurious wake-ups. See http://en.cppreference.com/w/cpp/thread/condition_variable for more details. – Maxim Egorushkin Jul 29 '15 at 17:44
-1

The producer in your code do a unlock before the notify_one. See Do I have to acquire lock before calling condition_variable.notify_one()? for the performance issue of holding/releasing lock. And see Sync is unreliable using std::atomic and std::condition_variable for a rare case race condition if you do not use lock in your producer. Read and justify if you want to keep the lock in producer, or not using the lock for performance.

Community
  • 1
  • 1
simon
  • 371
  • 1
  • 9