Help with threads in a metronome [c++]

I want to make a basic metronome in console, at least, cuz I don't know anything about gui in c++. The thing is I have made one in C# without much hassle, applying my meager java knowledge. But when I try to do it with c++ I seem not to get it, and I don't find much about it online.
At first, it doesn't need that much precision. I was thinking in how I did it with C#: the main console thread--that is somewhat implicit--and the one that makes the sound. I would like to do it this way so I can stop the metronome thread, and, perhaps, change the bpm and start again without having to compile again. So the main thread would have to kill the other one.
I would gladly accept links or commentaries that would guide me cuz I don't much about threads, or objects for that matter , in c++.

PS: this is my first forum topic/entry, am I doing well?

1 Like

Doing well. This is outside my zone of expertise, so I can't really help you too much on that front.

http://www.tutorialspoint.com/cplusplus/cpp_multithreading.htm

If you haven't seen this yet, have a look here. This should get you started, and should help you narrow your quest down from there.

For the record, the "deveopment" category on the forum is relatively, uh, inactive. So you may not see too many responses here. I'll do my best to help, but my expertise is not in C++.

2 Likes

If you can use C++ 11 or newer, then C++ has built in thread support: http://www.cplusplus.com/reference/thread/thread/. There is a post that has been active the last week about starting out in C++, you should check that out to get some recommendations on beginning C++.

3 Likes

Does C++11 have any new fucntion around threads? (I'll have a look at the link later when I'm off mobile, but a quick rundown would be awesome)

1 Like

C++ threading is very open, so you have to pick the right tool in the toolbox to solve the job. A lot has been written about this, and I'm the wrong guy to talk about all options, but this is what I would do.

My tool of choice would be C++11's std::thread library. We can create a thread by calling the constructor for std::thread with a function, this should be familiar to you if you've come from C#. So there are two things you'll need within that function for your plan to work.

  1. The function will need to take an argument specifying the rate of beats.
  2. You will need a flag accessible from both threads to indicate when the thread should stop.

A function signature such as void metronome(int beatsPerMinute) would work, however, to avoid using a global flag, we'll pass a reference to our flag as an argument to the function as well. This makes the function signature look like this: void metronome(int beatsPerMinute, const bool &stopFlag).

The thread will only be observing the flag, so we can pass by const ref and avoid using std::atomic, however, there is a caveat to using this. std::thread's constructor will call the copy constructor for every argument we provide, meaning changes to the flag outside the metronome thread will not be observed. To save ourselves, when calling this function make sure you call it with std::ref(myFlag) instead of just myFlag. Another way this could be solved is with something like std::shared_ptr, or via the use of a functor.

The body for this code isn't hard. See if you can figure it out, but if not, here's something that should suit your needs. By the way; you can use std::chrono::duration for measuring time intervals accurately.


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

using namespace std::literals::chrono_literals;

void metronome(int beatsPerMinute, const bool &stopFlag)
{
    while (!stopFlag)
    {
        std::cout << "beep boop\n";
        std::this_thread::sleep_for(1.0min / beatsPerMinute);
    }
}

int main()
{
    bool stop = false;

    std::thread mthread(metronome, 60, std::ref(stop));
    std::this_thread::sleep_for(0.25min);

    stop = true;
    mthread.join();
    
    return 0;
}
2 Likes

Thanks for the example (this was useful for me), also learned i need -lpthread when compiling things with threads (or -pthread, might need to read up on what they both do)

I think pthread is depreciated in C++11

Some reading suggests -lpthread links the thread lib, and -pthread is a platform specific compiler flag that links the lib and some marcos as well. Cant find a definitive answer on it except some passing comments to use -lpthread

1 Like

-pthread and -lpthread both cause the pthread library to be linked, while -pthread also causes some other changes along with that. So what is the pthread library? It's short for posix threads, an old and storied threading library. The only reason you would need to include this flag is if your implementation of the standard library was depending on posix threads. You can still use posix threads today, but you'll probably find that std::thread makes for easier to read code. You'll also find that most posix thread code is written for *nix systems, while older Windows oriented code tended to use Microsoft C APIs such as CreateThread.

Further Reading:



Ok, I've come up with this, but I'm not sure about leaks:

here's main:

    #include "Hilo.h"

    void mme(const bool& st, Hilo a){
        a.playTune(st);
    }

    int main() {
        {
            bool stop = false;
            Hilo* p = new Hilo;
            p->fillIn();
            std::thread t1(mme, std::ref(stop), *p);
            system("pause");
            stop = true;
            t1.join();
            delete p;
            p = nullptr;
        }
        return 0;
    }

here's Hilo.h

    #ifndef METRONOME_HILO_H
    #define METRONOME_HILO_H

    #include <iostream>
    #include <chrono>
    #include <windows.h>
    #include <thread>

    using std::cout; using std::cin; using std::endl;
    using namespace std::literals::chrono_literals;

    class Hilo {
    private:
        int frec1, frec2;
        float bpm, interval;
        inline void setInterval(float d){interval = d;}
        inline float getInterval(){ return interval;}
    public:
        inline void setFrec1(int a){frec1 = a;}
        inline void setFrec2(int b){frec2 = b;}
        inline void setBPM(float c){
            bpm = c;
            setInterval((60.0f/bpm)*1000);
        }

        inline int getFrec1(){return frec1;}
        inline int getFrec2(){return frec2;}
        inline float getBPM(){return bpm;}

        void fillIn();
        void fillInTwice();
        void playTune(const bool&);
        void playTunes(const bool&);

        Hilo();
    };
    #endif //METRONOME_HILO_H

and here's Hilo.cpp

    #include "Hilo.h"
    Hilo::Hilo() {
        setFrec1(0);
        setFrec2(0);
        setBPM(0);
        setInterval(0);
    }

    void Hilo::fillIn() {
        int a; bool b;
        do {
            b = false;
            cout << "Enter the frequency: ";
            if (!(cin >> a)) {
                cin.clear();
                cin.ignore(100, '\n');
                cout << "Invalid freq" << endl;
                b = true;
            }
        }while(b);

        float f; bool g;
        do{
            g = false;
            cout << "Enter them BPM: ";
            if(!(cin >> f)){
                cin.clear();
                cin.ignore(100, '\n');
                cout << "Invalid BPM" << endl;
                g = true;
            }
        }while(g);

        setFrec1(a);
        setBPM(f);
    }

    void Hilo::playTune(const bool&stp){
        while(!stp) {
            Beep(getFrec1(), 100);
            std::this_thread::sleep_for(1.0min/getBPM());
        }
    }

BTW, Hilo is Thread in Spanish
Some data is there to make two different beeps in a for 4/4 time signature.
Sorry, don't know how to format the code for the forum :c

2 Likes

Looking good. Glad to see that you managed to extend it to do more. Code formatting is done with triple grave by the way.

```
So your code should sit between the triple graves like this.
```

Or you can also indent things 4 spaces. I prefer the graves.


I would advise staying away from new and delete. In general, you can normally do without them entirely. This can be done by creating the objects on the stack rather than on the heap. E.G:

        Hilo myHilo;
        myHilo.fillIn();
        std::thread t1(mme, std::ref(stop), myHilo);

To use this code you also have to remove the call to delete and the assignment to nullptr. This creates your Hilo object on the stack, which means as soon as you exit the main() function it will be deallocated for you safely. This is safer because if an exception was thrown after you created your Hilo object pointer p and before it was deleted, that memory would leak.

This should also make you wonder, "Why did I use a pointer to begin with?" in particular look at this line of code: std::thread t1(mme, std::ref(stop), *p); This is dereferencing the pointer p to your Hilo object which is allocated on the heap, and then passing it to your function via the std::thread constructor. First std::thread will call Hilo's copy constructor, and then as the object is passed to your function mme Hilo's move constructor will be called.

I won't go too far into what a copy or move constructor is, but if you make any changes to p after starting the thread you will notice that they are not observed by the thread running mme, because the copy constructor has instantiated a complete clone of the object. To demo this, let's write an explicit copy/move constructor for Hilo (right now the compiler is writing one for you).

    Hilo();
    Hilo(Hilo const &aOther);
    Hilo(Hilo &&aOther);

Hilo::Hilo(Hilo const &aOther) :
    frec1(aOther.frec1),
    frec2(aOther.frec2),
    bpm(aOther.bpm),
    interval(aOther.interval)
{
    cout << "Copy constructor called.\n";
}

Hilo::Hilo(Hilo &&aOther) :
    frec1(aOther.frec1),
    frec2(aOther.frec2),
    bpm(aOther.bpm),
    interval(aOther.interval)
{
    cout << "Move constructor called.\n";
    // Note that std::move is often used here rather than copying, but the members of Hilo are all allocated on the stack if Hilo was, meaning we don't have any pointers to move.
}

Now when I run the program I get:

Enter the frequency: 100
Enter them BPM: 120
Copy constructor called.
Move constructor called.
Move constructor called.

As you can see, the copy constructor was called when the dereference Hilo (*p) was passed to std::thread, and after that, it was moved twice. Moving as you might imagine is better than copying in general for performance, however, performance is a very complex topic that I can't accurately cover in this post. So, why two moves? I'd put money on the first move taking place within the implementation of std::thread, after that the other move was when mme was called. mme takes Hilo by value (no reference qualifier or constness or pointer is specified in the function signature) so this allowed the compiler to simply move it's Hilo into that slot for you. This was possible because at that point (after the copy was made) that object was an rvalue.


So, that was a lot of information. I want to add one more thing however.

What could you do other than creating on the stack and other than calling three constructors for Hilo to get it to that thread? The answer here is in pointers. I did say no use of new or delete, but I didn't ban pointers! Specifically smart pointers such as std::shared_ptr and std::unique_ptr. In this case it's a shared resource, so you can create a Hilo with a shared pointer as follows:
#include <memory>
auto myHilo = std::make_shared<Hilo>();

From there you can use your regular dereferencing operators (* or ->) as per your needs just as you did with p, however, you do not dereference it to pass the argument to std::thread. At this point you modify mme to accept a shared_ptr as well:
void mme (const bool& st, std::shared_ptr<Hilo> a) {

Now we're cooking with gas. When you create your thread now, no copy or move constructors (for Hilo) will be called. Best of all, shared_ptr will manage your resource for you so there are no leaks, and you don't have to call delete. It's almost as easy as Java!

Granted, the shared pointer will instead be copied, and then moved twice just like before. But normally a pointer (even a smart one) is smaller than the object it is pointing to.

Good luck in your future endeavours.

1 Like

thank you.

Just got some time to write a more generic implementation of a timer work thread. I'm sure this could still be improved but it should give you an idea of how you can encapsulate your ideas:

#pragma once

#include <chrono>
#include <thread>

//
// This function will return a worker thread that will call 'func' every 'period'.
// The thread will loop until 'func' returns false.
//
// The callable 'func' must take a duration (in milliseconds) as its first argument.
// This duration will contain the period of elapsed time since the last call.
//
template <
	bool skip = false,
	class Duration,
	class Func,
	class... Args,

	// Only accept callable objects that return a boolean.
	class = typename std::enable_if<
		std::is_same<typename std::result_of<Func(std::chrono::milliseconds, Args...)>::type, bool>::value
	>::type
>
std::thread TimerWorkThread(Duration const &period, Func const &func, Args&&... args)
{
	typedef std::chrono::steady_clock clock;

	return std::thread([&]()
	{
		// We need a time point to work from and a reference to measure against.
		auto tick = clock::now();
		auto reference = clock::now();

		// Loop forever.
		while (true)
		{
			// Calculate the period of time since we last called func. Move our reference forwards.
			auto passed = std::chrono::duration_cast<milliseconds>(clock::now() - reference);
			reference = clock::now();

			// Call func will passed and any args we got. If it returns false, stop.
			if (!func(passed, std::forward<Args>(args)...))
				return;

			// If we are skipping cycles during stress then loop over the sleep.
			do
			{
				// Figure out when we need to sleep to.
				tick += period;
			} while (skip && (clock::now() - tick) > period);

			std::this_thread::sleep_until(tick);
		}
	});
};

Read the comments in the code for a description of its use, I'll also put some example calling code here:

#include <iostream>

#include "TimerWorkThread.h"

using namespace std;
using namespace std::chrono;
using namespace std::literals::chrono_literals;

int main()
{
	int iterations = 45;

	auto t = TimerWorkThread(1s, [&](milliseconds tick)
	{
		auto now = system_clock::to_time_t(system_clock::now());
		cout << std::put_time(std::localtime(&now), "%F %T") << " Elapsed: " << tick.count() << "ms" << endl;

		return --iterations > 0;
	});

	t.join();
    return 0;
}

As you can see you can simply pass a lambda to it, however, you could also use a function if you want.

The best thing about this timer as compared to the code we've talked about so far is it won't deviate over time. Thread sleeping can be fairly unreliable and may deviate milliseconds at a time every sleep. Additionally, we weren't taking into account how long the inner work took to execute. This method keeps track of the time it should be sleeping so that it does not deviate.

In comparison to our older code, you also have the superior pattern of stopping when your loop body function returns false. This means you don't have to worry about pointers or references quite as much. This method also provides the actual time passed between function calls, allowing you to adjust time sensitive operations every iteration. This is common with the pattern of a game loop.

Lastly, you can enable the ability to skip cycles when longer than the period elapsed by specifying in the template parameters when calling TimerWorkThread. E.G. TimerWorkThread<true>(5.5s, ...).

Sample output:

2017-02-12 13:57:03 Elapsed: 0ms
2017-02-12 13:57:04 Elapsed: 1001ms
2017-02-12 13:57:05 Elapsed: 999ms
2017-02-12 13:57:06 Elapsed: 999ms
2017-02-12 13:57:07 Elapsed: 1000ms
2017-02-12 13:57:08 Elapsed: 999ms

Again, this is probably not the best approach, I'm still learning modern C++ techniques, and I'm not sure my template is the best it could be. The above code is provided under the WTF public license which you can find here: http://www.wtfpl.net/ which means you can copy it, edit it, distribute it, whatever. Just don't blame me if it does something wrong. :)

1 Like

me not knows anything about templates :c
I'll check it out and then try yer code.

If you want any information on templates hit me up. I'm still learning, but it's a vast topic and there is a lot I have learned that I can share.

As a short summary: In Java or C# you will have access to generics. Generics are much simpler, so let's start there and build an analogy:

Generics allow you to specify that a class can be used by any type rather than just one type. The best example in my mind being generic collections, List<>, for instance, is a generic class. What this is doing is fairly obvious; creating a collection that works with any type and allowing you to specify the type you want at compile time, thus benefiting from compile time type awareness.

If you are comfortable with that, then you are probably aware of a limitation of generics: If you were writing a mathematics library and you wanted to write a function that worked for both floating point numbers and integers, you would find that generics are really terrible for solving that problem. The generic class will complain that not every type that you could substitute in is guaranteed to have basic operations, for instance. You would essentially have to cast the type at some point to deal with this problem.

C++ has no such limitation in templates. The reason for this is that C++ evaluates templates only when you fill in the template parameters. This means that if you put <double> in your template then the compiler will apply the rules of the function to that templated version of the function. C++ also allows for much more complex template statements which are referred to as "template metaprograming".

The enable_if nonsense at the end does what the comment above it states, it prevents you from providing a function to TimerWorkThread that does not return a boolean. There are other ways to do this, but I find that simply accepting any type for func and then using the template or the function body to enforce that it returns bool is the most reliable.

Without the enable_if you could pass a function that returns anything that could be implicitly cast to a boolean instead. This is because we use func in an if statement within the body of the function. This would be acceptable, but it provides an interface that I would consider worse since it doesn't inform the user of the expectations on the function.