Sleep and wait

Contents[Show]

The new time library is an important component of the threading interface. As well threads, locks, condition variables as future have an understanding of time. All four have in common that they can sleep, wait or block until a time point or for a time duration.

Convention

The methods for the handling of time in multithreading programs follow a simple convention. Methods ending with _for have to be parametrized by a time duration; methods ending with _until by a time point. I presented time duration and time point in separate posts. Here is a concise overview of the methods dealing with sleeping, blocking, and waiting.

untilAndForEng

in2min stand for a time 2 minutes in the future. 2s is a time duration of 2 seconds. Although I use auto it's very verbose to define the time point in2min: auto in2min= std::chrono::steady_clock::now() + std::chrono::minutes(2). To the rescue we have time literals in C++14 2s is valid C++14. C++14 has more literals for typical time durations.

Now the practice.

Various waiting strategies

First, I want to describe the various waiting strategies.

The key idea of the program is that the promise provides its result for four shared futures. That's possible because I used std::shared_future. Each future has a different waiting strategy. All promise and future will be executed in different threads. For the sake of simplicity I will in the rest of the posts only speak about a waiting thread although the future is indeed waiting. You can read the details about promise and future here.

 

  • consumeThread1: Waits up to 4 seconds for the result of the promise.
  • consumeThread2: Waits up to 20 seconds for the result of the promise.
  • consumeThread3: Asks the promise for the result and goes back to sleep for 700 milliseconds.
  • consumeThread4: Asks the promise for the result and goes back to sleep. It's sleep duration starts with 1 millisecond and doubles each time.

Here is the program.

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
// sleepAndWait.cpp

#include <utility>
#include <iostream>
#include <future>
#include <thread>
#include <utility>

std::mutex coutMutex;

long double getDifference(const std::chrono::steady_clock::time_point& tp1,const std::chrono::steady_clock::time_point& tp2){
    auto diff= tp2- tp1;
    auto res= std::chrono::duration <double, std::milli> (diff).count();
    return res;
}

voidproducer(std::promise<int>&& prom){
    std::cout << "PRODUCING THE VALUE 2011\n\n"; 
    std::this_thread::sleep_for(std::chrono::seconds(5));
    prom.set_value(2011);
}

void consumer(std::shared_future<int> fut,std::chrono::steady_clock::duration dur){
    auto start = std::chrono::steady_clock::now();
    std::future_status status= fut.wait_until(std::chrono::steady_clock::now() + dur);
    if ( status == std::future_status::ready ){
        std::lock_guard<std::mutex> lockCout(coutMutex);
        std::cout << std::this_thread::get_id() << " ready => Result: " << fut.get() << std::endl;
    }
    else{
        std::lock_guard<std::mutex> lockCout(coutMutex);
        std::cout << std::this_thread::get_id() << " stopped waiting." << std::endl;
    }
    auto end= std::chrono::steady_clock::now();
    std::lock_guard<std::mutex> lockCout(coutMutex);
    std::cout << std::this_thread::get_id() << " waiting time: " << getDifference(start,end) << " ms" << std::endl;
}

void consumePeriodically(std::shared_future<int> fut){
    auto start = std::chrono::steady_clock::now();
    std::future_status status;
    do {
        std::this_thread::sleep_for(std::chrono::milliseconds(700));
        status = fut.wait_for(std::chrono::seconds(0));
        if (status == std::future_status::timeout) {
            std::lock_guard<std::mutex> lockCout(coutMutex);
            std::cout << "     " << std::this_thread::get_id() << " still waiting." << std::endl;
        }
        else if (status == std::future_status::ready) {
            std::lock_guard<std::mutex> lockCout(coutMutex);
            std::cout << "     " << std::this_thread::get_id() << " waiting done => Result: " << fut.get() << std::endl;
        }
    } while (status != std::future_status::ready); 
    auto end= std::chrono::steady_clock::now();
    std::lock_guard<std::mutex> lockCout(coutMutex);
    std::cout << "     " << std::this_thread::get_id() << " waiting time: " << getDifference(start,end) << " ms" << std::endl;
}

void consumeWithBackoff(std::shared_future<int> fut){
    auto start = std::chrono::steady_clock::now();
    std::future_status status;
    auto dur= std::chrono::milliseconds(1);
    do {
        std::this_thread::sleep_for(dur);
        status = fut.wait_for(std::chrono::seconds(0));
        dur *= 2;
        if (status == std::future_status::timeout) {
            std::lock_guard<std::mutex> lockCout(coutMutex);
            std::cout << "         " << std::this_thread::get_id() << " still waiting." << std::endl;
        }
        else if (status == std::future_status::ready) {
            std::lock_guard<std::mutex> lockCout(coutMutex);
            std::cout << "         " << std::this_thread::get_id() << " waiting done => Result: " << fut.get() << std::endl;
        }
    } while (status != std::future_status::ready);
    auto end= std::chrono::steady_clock::now();
    std::lock_guard<std::mutex> lockCout(coutMutex);
    std::cout << "         " << std::this_thread::get_id() << " waiting time: " << getDifference(start,end) << " ms" << std::endl;
}

int main(){
    
    std::cout << std::endl;

    std::promise<int> prom;
    std::shared_future<int> future= prom.get_future();
    std::thread producerThread(producer,std::move(prom));
    
    std::thread consumerThread1(consumer,future,std::chrono::seconds(4));
    std::thread consumerThread2(consumer,future,std::chrono::seconds(20));
    std::thread consumerThread3(consumePeriodically,future);
    std::thread consumerThread4(consumeWithBackoff,future);
    
    consumerThread1.join();
    consumerThread2.join();
    consumerThread3.join();
    consumerThread4.join();
    producerThread.join();
    
    std::cout << std::endl;

}

 

I create in the main function (line 85) the promise, use the promise to create the associated future (line 86) and move the promise in a separate thread (line 87). I have to move the promise in the thread because it doesn't support copy semantic. That will not hold for the shared futures (lines 89 - 92). They support copy semantic and can therefore be copied.

Before I talk about the work package of the thread, let me say a few words about the auxiliary function getDifference (line 11 - 15). The function takes two time points and returns the time duration in milliseconds. I will use the function a few times. 

What's about the threads?

  • producerThread: Executes the function producer (line 17 - 21) and publishes its result 2011 after a 5 seconds of sleeping. This is the result the futures are waiting for.
  • consumerThread1: Executes the function consumer (line 23 - 37). The thread is waiting for at most 4 seconds (line 25) before it continues with its work. This waiting period is not long enough to get the result for the promise.
  • consumerThread2: Executes the function consumer (line 23 - 37). The thread is waiting at most 20 seconds before it continues with its work.
  • consumerThread3:Executes the function consumePeriodically (line 39 - 57). It sleeps for 700 milliseconds (line 43) and asks for the result of the promise (line 44). Because of the 0 seconds in line 44 (std::chrono::seconds(0)) there is no waiting. If the result of the calculation is available, it will be displayed in line 73.
  • consumerThread4:Executes the function consumeWithBackoff (line 59 - 79). It sleeps in the first iteration 1 second and doubles by each further iteration its sleeping period. Otherwise, its strategy is similar to consumerThread3.

Now to the synchronization of the program. As well the clock for determining the current time as std::cout are shared variables. But I need no synchronization. First, the method call std::chrono::steady_clock::now() is thread-safe (for example in line 24 and 34), second, the C++ runtime guarantees that the characters will be written thread-safe to std::cout. Only for visual reason I used a std::lock_guard to wrap std::cout (for example in line 27,31, and 35).


Although the threads write one after the other to std::cout the output is not easy to understand.


sleepAndWait

The first output is from the promise. The remaining outputs from the futures. At first consumerThread4 asks for the result. The output is indented by 8 characters. consumerThread4 also displays its ID. consumerThread3 is immediately following. Its output is indented by 4 characters. The output of consumerThread1 and consumerThread2 is not indented.

  • consumeThread1: Waits unsuccessfully 4000.18 ms seconds without getting the result.
  • consumeThread2: Gets the result after 5000.3 ms although it's waiting duration is up to 20 seconds.
  • consumeThread3: Gets the result after 5601.76 ms. That's about 5600 milliseconds= 8*7000 milliseconds.
  • consumeThread4: Gets the result after 8193.81 ms. To say it differently. It waits 3 seconds to long. 

What's next?

This posts finishes my miniseries about the time library. In the next post I will write about embedded programming with modern C++. Here is the overview of my plan.

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Get your e-book. Support my blog.

 

Tags: time

Comments   

0 #1 Alexandria 2016-12-25 03:41
Good response in return of this question with genuine arguments and
telling everything oon the topic of that.
Quote
0 #2 86Teresa 2017-07-26 05:50
I see you don't monetize your page, don't waste your traffic, you can earn additional cash every month because your content is good quality.
If you want to know what is the best adsense alternative, search in google: murgrabia's tools
Quote

Add comment


My Newest E-Books

Latest comments

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 511

All 460340

Currently are 220 guests and no members online