r18ul
r18ul

Reputation: 1064

How do I use sleep time in my program?

I have written a sample program on multithreading for practice. I'm in a dilemma on the place to use the sleep_for(milliseconds) function. I'm trying to learn the threading concepts. So consider this as my practice to see how the three threads are synchronized. I added sleep for the sake of knowing about it. Also, my intention to add sleep is to see the output more clearly on the console.

Kindly help me learn about how do I decide on in which function should I use the sleep_for.

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

using namespace std;
using std::chrono::duration;
using std::chrono::system_clock;

void oneLine(mutex & mut, int nr)
{
    lock_guard<mutex> lg(mut);

    cout << "Thread # " << nr << endl;

    // Should I call sleep_for here?
    std::this_thread::sleep_for(chrono::milliseconds(25)); 
}

void callFun(mutex & mut, int nr)
{
    while(true)
    {
        oneLine(mut, nr);
        this_thread::yield();

        // or here?
        std::this_thread::sleep_for(chrono::milliseconds(250));
    }
}

int main()
{
    mutex thMutex;
    thread t1(callFun, ref(thMutex), 1);
    thread t2(callFun, ref(thMutex), 2);
    thread t3(callFun, ref(thMutex), 3);
    t1.join();
    t2.join();
    t3.join();
}

Where can I call sleep_for in oneLine() or inside callFun(). Thanks.

Upvotes: 1

Views: 553

Answers (2)

9Breaker
9Breaker

Reputation: 724

I think your example is great to learn the basics of multithreading. It all comes down to how you want to design the code for Case 1 or Case 2 (see labels below in code).

For Case 1, you are calling the sleep in your oneLine function so that it will be called before the thread is yielded and before the mutex lock is out of scope, thus preventing the next thread from finishing the oneLine function until it can acquire the lock on the mutex. If you care that there is a pause before the next thread can acquire the mutex, then choose Case 1. It is a design decision and up to you for your application in your code. In Case 2, if you want to have another thread finish its oneLine call and not have to wait the sleep period you specified for the other thread to relinquish the lock on the mutex, then put it after the function. In Case 2, you just care that there is a fixed pause between the same thread being run.

EDIT: However, with all of the cases, there is no guarantee that these threads will access the mutex in any predefined order. I ran the code with the sleep for Case 1, Case 2, and with no sleep and the order is not guaranteed to be sequential for the mutex lock.

Just a note on std::this_thread::yield from http://en.cppreference.com/w/cpp/thread/yield

The exact behavior of this function depends on the implementation, in particular on the mechanics of the OS scheduler in use and the state of the system. For example, a first-in-first-out realtime scheduler (SCHED_FIFO in Linux) would suspend the current thread and put it on the back of the queue of the same-priority threads that are ready to run (and if there are no other threads at the same priority, yield has no effect).

So the behavior of your code would depend on the OS scheduler too. In my case, when I ran it, the mutex locks were never in a definite order. See the screenshot below where the order was not sequential.

Not sequential in order

    #include <iostream>
    #include <chrono>
    #include <thread>
    #include <mutex>
    #include <stdlib.h> //needed added here


using std::chrono::duration;
using std::chrono::system_clock;

void oneLine(std::mutex & mut, int nr)
{
    std::lock_guard<std::mutex> lg(mut);

    std::cout << "Thread # " << nr << std::endl;

    // Case1
    std::this_thread::sleep_for(std::chrono::milliseconds(2500));
}

void callFun(std::mutex & mut, int nr)
{
    while(true)
    {
        oneLine(mut, nr);
        std::this_thread::yield();

        // Case2
        //std::this_thread::sleep_for(std::chrono::milliseconds(250));
    }
}

int main(int argc, char **argv)
{
    std::mutex thMutex;
    std::thread t1(callFun, ref(thMutex), 1);
    std::thread t2(callFun, ref(thMutex), 2);
    std::thread t3(callFun, ref(thMutex), 3);
    t1.join(); // will never get here, because thread t1 in infinite loop
    t2.join(); // will never get here, because thread t2 in infinite loop
    t3.join(); // will never get here, because thread t3 in infinite loop
    return(0); // will never get here because waiting for threads t1, t2, t3 to finish
}

Upvotes: 3

Solomon Slow
Solomon Slow

Reputation: 27115

Your question boils down to this: Should you call sleep() with the mutex locked? or with the mutex unlocked?

That depends on what you want to simulate. Do you want to simulate something that does not happen very often? Then sleep() outside the protected block. If you want to simulate a synchronized operation that takes a long time, then sleep() inside the protected block.

But Note!

Any real program that keeps a mutex locked for more than a few microseconds is a big red flag for design review. A program that keeps a mutex locked for an extended amount of time is unlikely to perform well.

And Never,...

Ever,...

...sleep() inside a mutex in any real program. Just don't, OK?

Upvotes: 2

Related Questions