COMS W4995 C++ Deep Dive for C Programmers

Index of 2025-9/code/18

Parent directory
Makefile
concur.cpp
cond-var.cpp
pthread.cpp

Makefile

CC  = g++
CXX = g++

CFLAGS   = -Wall -g
CXXFLAGS = -Wall -g -std=c++20 -pthread

LDFLAGS  = -pthread
LDLIBS   =

executables = pthread concur cond-var

.PHONY: default
default: $(executables)

pthread: pthread.o

concur: concur.o

cond-var: cond-var.o

.PHONY: clean
clean:
	rm -f a.out core *.o $(executables)

.PHONY: all
all: clean default

concur.cpp

#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
using namespace std::chrono;

struct wallet {
    mutex m;
    uint64_t money = 0;
    operator uint64_t() const { return money; }

    wallet& operator++() {
        m.lock();
        ++money;
        m.unlock();

        // {
        //     scoped_lock lck{m};
        //     ++money;
        // }

        return *this;
    }
};

void f(wallet& sum, uint64_t count) {
    while (count--) {
        ++sum;
    }
}

int main() {
    constexpr uint64_t count = 1'000'000;
    wallet sum;

    auto time0 = high_resolution_clock::now();

    thread t1 { f, ref(sum), count };
    thread t2 { f, ref(sum), count };
    t1.join();
    t2.join();

    // {
    //     jthread t1 { f, ref(sum), 1'000'000 };
    //     jthread t2 { f, ref(sum), 1'000'000 };
    // }

    auto time1 = high_resolution_clock::now();
    auto dt = duration_cast<microseconds>(time1 - time0);
    cout << "elapsed: " << dt.count() << " microsec\n";
    cout << "result: " << sum << '\n';
}

/*

Lecture outline:

1. std::thread and std::mutex

2. std::scoped_lock

3. std::jthread
 
*/

cond-var.cpp

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdlib>
#include <cassert>
#include <random>
#include <thread>
#include <deque>
#include <mutex>
#include <condition_variable>
using namespace std;

int main() {
    vector<string> vec;

    mutex mtx;
    condition_variable got_something;

    thread t {
        [&]() {
            while (1) {
                unique_lock lck(mtx);  // lck's contructor will lock mtx

                while (vec.empty()) {
                    got_something.wait(lck);  // wait will release lck
                }
                // lck is reacquired when wait() returns

                for (const auto& x : vec) {
                    // exit thread on poison value
                    if (x == ""s) {
                        return;
                    }
                    cout << x << ' ';
                }
                cout << '\n';
                vec.clear();

                // lck's destructor will unlock mtx
            }
        }
    };

    string str, line;
    while (getline(cin, line)) {
        istringstream iss(line);

        unique_lock lck(mtx);  // lck's contructor will lock mtx

        while (iss >> str) { vec.push_back(str); }

        got_something.notify_one();  // unblock one waiting thread

        // lck's destructor will unlock mtx
    }

    {
        unique_lock lck(mtx);
        vec.push_back(""s);  // poison value to tell the thread to exit
        got_something.notify_one();
    }
    t.join();
}

pthread.cpp

#include <iostream>
#include <chrono>
#include <pthread.h>
using namespace std;
using namespace std::chrono;

struct Args {
    uint64_t* sum_ptr;
    uint64_t count;
};

void* f1(void* args) {
    Args* p = (Args*)args;
    uint64_t* sum_ptr = p->sum_ptr;
    uint64_t count = p->count;

    while (count--) {
        ++*sum_ptr;
    }
    return NULL;
}

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

void* f2(void* args) {
    Args* p = (Args*)args;
    uint64_t* sum_ptr = p->sum_ptr;
    uint64_t count = p->count;

    while (count--) {
        pthread_mutex_lock(&mtx);
        ++*sum_ptr;
        pthread_mutex_unlock(&mtx);
    }
    return NULL;
}

uint64_t sum1() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;

    Args args1 { &sum, count };
    f1(&args1);

    Args args2 { &sum, count };
    f1(&args2);

    return sum;
}

uint64_t sum2() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;
    pthread_t t1, t2;

    Args args1 { &sum, count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum, count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum;
}

uint64_t sum3() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum[2] = {0};
    pthread_t t1, t2;

    Args args1 { &sum[0], count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum[1], count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum[0] + sum[1];
}

uint64_t sum4() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum[9] = {0};
    pthread_t t1, t2;

    Args args1 { &sum[0], count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum[8], count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum[0] + sum[8];
}

uint64_t sum5() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;
    pthread_t t1, t2;

    Args args1 { &sum, count };
    pthread_create(&t1, NULL, &f2, &args1);

    Args args2 { &sum, count };
    pthread_create(&t2, NULL, &f2, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum;
}

int main() {
    auto time0 = high_resolution_clock::now();

    uint64_t result = sum1();
    // uint64_t result = sum2();
    // uint64_t result = sum3();
    // uint64_t result = sum4();
    // uint64_t result = sum5();

    auto time1 = high_resolution_clock::now();
    auto dt = duration_cast<microseconds>(time1 - time0);
    cout << "elapsed: " << dt.count() << " microsec\n";
    cout << "result: " << result << '\n';
}