From a75329668e0f31f6c0c58235458150795b1f9f25 Mon Sep 17 00:00:00 2001 From: Zhihua Lai Date: Mon, 26 Jan 2026 12:09:24 +0000 Subject: [PATCH 1/2] Add multithreaded example --- README.md | 4 +- multithread-sum/Makefile | 30 ++++++++++++++ multithread-sum/main.cpp | 84 ++++++++++++++++++++++++++++++++++++++++ multithread-sum/tests.sh | 5 +++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 multithread-sum/Makefile create mode 100644 multithread-sum/main.cpp create mode 100755 multithread-sum/tests.sh diff --git a/README.md b/README.md index cc0db73..082497b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ This repository is intended as: Examples include (and will expand to): -* [thread-safe-queue](./thread-safe-queue/) +* Multithreading + * [thread-safe-queue](./thread-safe-queue/) + * [multithread-sum](./multithread-sum/) * Smart pointers * [unique-ptr-basics](./unique-ptr-basics/) * [smart-ptr](./smart-ptr/) diff --git a/multithread-sum/Makefile b/multithread-sum/Makefile new file mode 100644 index 0000000..172da2f --- /dev/null +++ b/multithread-sum/Makefile @@ -0,0 +1,30 @@ +# pull in shared compiler settings +include ../common.mk + +# per-example flags +# CXXFLAGS += -pthread + +## get it from the folder name +TARGET := $(notdir $(CURDIR)) +SRCS := $(wildcard *.cpp) +OBJS := $(SRCS:.cpp=.o) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $^ + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +run: $(TARGET) + ./$(TARGET) $(ARGS) + +clean: + rm -f $(OBJS) $(TARGET) + +# Delegates to top-level Makefile +check-format: + $(MAKE) -f ../Makefile check-format DIR=$(CURDIR) + +.PHONY: all clean run check-format diff --git a/multithread-sum/main.cpp b/multithread-sum/main.cpp new file mode 100644 index 0000000..6f3c6ef --- /dev/null +++ b/multithread-sum/main.cpp @@ -0,0 +1,84 @@ +/** +This demonstrates a simple multithreaded sum calculation using C++11 threads. +*/ + +#include +#include +#include +#include +#include +#include // for exceptions +#include + +using SumT = unsigned long long; + +int main(int argc, char* argv[]) +{ + std::size_t data_size = 1'000'000; + std::size_t num_threads = 4; + + if (argc == 3) { + try { + long long ds = std::stoll(argv[1]); + long long nt = std::stoll(argv[2]); + if (ds < 0 || nt <= 0) { + throw std::invalid_argument("data_size must be >= 0 and num_threads must be > 0"); + } + data_size = static_cast(ds); + num_threads = static_cast(nt); + } catch (const std::exception& e) { + std::cerr << "Invalid arguments: " << e.what() << "\n" + << "Usage: " << argv[0] << " \n"; + return 1; + } + } else if (argc != 1) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + if (num_threads == 0) return 1; + if (data_size == 0) { + std::cout << "Total Sum: 0\nExpected Sum: 0\n"; + return 0; + } + + // Avoid spawning more threads than elements (optional but sensible). + if (num_threads > data_size) num_threads = data_size; + + // Guard against int overflow in iota values. + if (data_size > static_cast(std::numeric_limits::max())) { + std::cerr << "data_size too large for vector initialization via iota.\n"; + return 1; + } + + std::vector data(data_size); + std::iota(data.begin(), data.end(), 1); + + std::vector partial_sums(num_threads, 0); + std::vector threads; + threads.reserve(num_threads); + + const std::size_t block_size = data_size / num_threads; + + for (std::size_t i = 0; i < num_threads; ++i) { + const std::size_t start = i * block_size; + const std::size_t end = (i == num_threads - 1) ? data_size : start + block_size; + + threads.emplace_back([&, i, start, end] { + partial_sums[i] = std::accumulate(data.begin() + start, data.begin() + end, SumT{0}); + std::cout << "Thread processing range [" << start << ", " << end + << ") computed local sum: " << partial_sums[i] << "\n"; + }); + } + + for (auto& t : threads) t.join(); + + const SumT global_sum = std::accumulate(partial_sums.begin(), partial_sums.end(), SumT{0}); + std::cout << "Total Sum: " << global_sum << "\n"; + + const SumT expected_sum = static_cast(data_size) * (static_cast(data_size) + 1) / 2; + std::cout << "Expected Sum: " << expected_sum << "\n"; + + assert(expected_sum == global_sum); + return 0; +} diff --git a/multithread-sum/tests.sh b/multithread-sum/tests.sh new file mode 100755 index 0000000..4a4689d --- /dev/null +++ b/multithread-sum/tests.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -ex + +./multithread-sum 1000000 4 \ No newline at end of file From 2dc72c915c7b88f5d6296a0a5aa8016e83944acf Mon Sep 17 00:00:00 2001 From: Zhihua Lai Date: Mon, 26 Jan 2026 12:11:13 +0000 Subject: [PATCH 2/2] Format --- multithread-sum/main.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/multithread-sum/main.cpp b/multithread-sum/main.cpp index 6f3c6ef..fa69a64 100644 --- a/multithread-sum/main.cpp +++ b/multithread-sum/main.cpp @@ -7,12 +7,13 @@ This demonstrates a simple multithreaded sum calculation using C++11 threads. #include #include #include -#include // for exceptions +#include // for exceptions #include using SumT = unsigned long long; -int main(int argc, char* argv[]) +int +main(int argc, char* argv[]) { std::size_t data_size = 1'000'000; std::size_t num_threads = 4; @@ -36,14 +37,16 @@ int main(int argc, char* argv[]) return 1; } - if (num_threads == 0) return 1; + if (num_threads == 0) + return 1; if (data_size == 0) { std::cout << "Total Sum: 0\nExpected Sum: 0\n"; return 0; } // Avoid spawning more threads than elements (optional but sensible). - if (num_threads > data_size) num_threads = data_size; + if (num_threads > data_size) + num_threads = data_size; // Guard against int overflow in iota values. if (data_size > static_cast(std::numeric_limits::max())) { @@ -71,7 +74,8 @@ int main(int argc, char* argv[]) }); } - for (auto& t : threads) t.join(); + for (auto& t : threads) + t.join(); const SumT global_sum = std::accumulate(partial_sums.begin(), partial_sums.end(), SumT{0}); std::cout << "Total Sum: " << global_sum << "\n";