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

using namespace std;

void serve_n(int i, int& counter) {
    counter += i;    counter += i;
}
void serve_a(int i, atomic<int>& counter) {
    counter += i;    counter += i;
}
void serve_m(int i, int& counter, mutex& m) {
    lock_guard<mutex> lock(m);
    counter += i;    counter += i;
}
void serve_m2(int i, int& counter) {
    mutex m;
    lock_guard<mutex> lock(m);
    counter += i;    counter += i;
}
int threads_free() {
    int counter = 0;
    thread th1( serve_n, 1, ref(counter) );
    thread th2( serve_n, 10, ref(counter)  );
    thread th3( serve_n, 100, ref(counter)  );
    th1.join(); th2.join(); th3.join();
    return counter;
}
int threads_atomic() {
    atomic<int> counter = 0;
    thread th1( serve_a, 1, ref(counter) );
    thread th2( serve_a, 10, ref(counter) );
    thread th3( serve_a, 100, ref(counter) );
    th1.join(); th2.join(); th3.join();
    return counter.load();
}
int threads_mutex() {
    int counter = 0;
    mutex m;
    thread th1( serve_m, 1, ref(counter), ref(m) );
    thread th2( serve_m, 10, ref(counter), ref(m) );
    thread th3( serve_m, 100, ref(counter), ref(m) );
    th1.join(); th2.join(); th3.join();
    return counter;
}
int threads_mutex2() {
    int counter = 0;
    thread th1( serve_m2, 1, ref(counter) );
    thread th2( serve_m2, 10, ref(counter) );
    thread th3( serve_m2, 100, ref(counter) );
    th1.join(); th2.join(); th3.join();
    return counter;
}


int main() {
    for(int i = 0; i < 10; ++i) {
        cout << threads_free() << endl;
    }
    for(int i = 0; i < 10; ++i) {
        cout << threads_atomic() << endl;
    }
    for(int i = 0; i < 10; ++i) {
        cout << threads_mutex() << endl;
    }
    for(int i = 0; i < 10; ++i) {
        cout << threads_mutex2() << endl;
    }

    // counter = 0;
    // async_no_mutex();
    // counter = 0;
    // async_mutex();
    // counter = 0;
    // threads_no_mutex();
    // counter = 0;
    // threads_mutex();
    return 0;
}

