S SmartDocs
系列: C++ cpp 381 行 · 更新于 2026-04-03

threads.cpp

C++/Part5_進階主題/Ch23_多執行緒程式設計/threads.cpp

// threads.cpp
// 編譯指令:g++ -std=c++17 -Wall -pthread threads.cpp -o threads
//
// 注意:多執行緒程式必須加上 -pthread 旗標
//
// 本程式示範 std::thread 的基本用法

#include <iostream>
#include <thread>
#include <vector>
#include <string>
#include <functional>
#include <sstream>
#include <mutex>
#include <chrono>

// 全域 mutex 用於同步化主控台輸出
std::mutex cout_mutex;

// 執行緒安全的輸出函式
template<typename... Args>
void safe_print(const Args&... args) {
    std::lock_guard<std::mutex> lock(cout_mutex);
    (std::cout << ... << args) << "\n";
}

// ============================================================
// 第一部分:基本執行緒建立
// ============================================================

// 一般函式作為執行緒任務
void hello_task() {
    safe_print("  [一般函式] Hello from thread! ID: ", std::this_thread::get_id());
}

// 帶參數的函式
void greet(int id, const std::string& name) {
    safe_print("  [帶參數] Thread ", id, " says: Hello, ", name, "!");
}

void demo_basic_threads() {
    std::cout << "========================================\n";
    std::cout << "  基本執行緒建立\n";
    std::cout << "========================================\n\n";

    // 方式 1:使用一般函式
    std::cout << "1. 使用一般函式:\n";
    std::thread t1(hello_task);
    t1.join();

    // 方式 2:使用 lambda
    std::cout << "\n2. 使用 lambda:\n";
    std::thread t2([]() {
        safe_print("  [Lambda] Hello from lambda thread! ID: ",
                   std::this_thread::get_id());
    });
    t2.join();

    // 方式 3:帶參數的函式
    std::cout << "\n3. 帶參數的函式:\n";
    std::thread t3(greet, 1, "Alice");
    std::thread t4(greet, 2, "Bob");
    t3.join();
    t4.join();

    // 方式 4:帶捕獲的 lambda
    std::cout << "\n4. 帶捕獲的 lambda:\n";
    int value = 42;
    std::thread t5([value]() {
        safe_print("  [Lambda 捕獲] 捕獲的值: ", value);
    });
    t5.join();

    std::cout << "\n";
}

// ============================================================
// 第二部分:使用成員函式
// ============================================================

class Worker {
    std::string name_;

public:
    explicit Worker(const std::string& name) : name_(name) {}

    void do_work(int task_id) {
        safe_print("  Worker '", name_, "' 正在處理任務 ", task_id);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        safe_print("  Worker '", name_, "' 完成任務 ", task_id);
    }

    void operator()(int repetitions) {
        for (int i = 0; i < repetitions; ++i) {
            safe_print("  Worker '", name_, "' 的函式物件呼叫 #", i + 1);
        }
    }
};

void demo_member_function() {
    std::cout << "========================================\n";
    std::cout << "  成員函式作為執行緒任務\n";
    std::cout << "========================================\n\n";

    Worker w1("小明");
    Worker w2("小華");

    // 傳遞成員函式:需要物件指標
    std::cout << "1. 使用成員函式:\n";
    std::thread t1(&Worker::do_work, &w1, 101);
    std::thread t2(&Worker::do_work, &w2, 102);
    t1.join();
    t2.join();

    // 使用函式物件(operator())
    std::cout << "\n2. 使用函式物件:\n";
    std::thread t3(Worker("函式物件Worker"), 3);
    t3.join();

    std::cout << "\n";
}

// ============================================================
// 第三部分:join vs detach
// ============================================================

void demo_join_detach() {
    std::cout << "========================================\n";
    std::cout << "  join vs detach\n";
    std::cout << "========================================\n\n";

    // join:主執行緒等待子執行緒完成
    std::cout << "1. join — 主執行緒等待:\n";
    std::thread t1([]() {
        safe_print("  子執行緒開始工作...");
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        safe_print("  子執行緒工作完成!");
    });
    safe_print("  主執行緒等待子執行緒...");
    t1.join();
    safe_print("  主執行緒確認子執行緒已完成\n");

    // detach:子執行緒獨立運行
    std::cout << "2. detach — 子執行緒獨立運行:\n";
    std::thread t2([]() {
        safe_print("  [分離的執行緒] 開始背景工作...");
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        safe_print("  [分離的執行緒] 背景工作完成");
    });
    t2.detach();
    safe_print("  主執行緒不等待,繼續執行");

    // 確認 joinable 狀態
    std::cout << "\n3. joinable 狀態檢查:\n";
    std::thread t3([]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    });
    safe_print("  建立後 joinable: ", t3.joinable() ? "是" : "否");
    t3.join();
    safe_print("  join 後 joinable: ", t3.joinable() ? "是" : "否");

    // 給 detached 執行緒一點時間完成
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "\n";
}

// ============================================================
// 第四部分:參數傳遞
// ============================================================

void modify_value(int& value) {
    value += 100;
}

void process_string(const std::string& str) {
    safe_print("  處理字串: \"", str, "\" (長度: ", str.size(), ")");
}

void demo_arguments() {
    std::cout << "========================================\n";
    std::cout << "  執行緒參數傳遞\n";
    std::cout << "========================================\n\n";

    // 值傳遞(預設行為)
    std::cout << "1. 值傳遞(預設):\n";
    int x = 42;
    std::thread t1([](int val) {
        safe_print("  收到值: ", val);
    }, x);
    t1.join();
    safe_print("  原始值不變: x = ", x);

    // 參考傳遞(需要 std::ref)
    std::cout << "\n2. 參考傳遞(std::ref):\n";
    int y = 42;
    safe_print("  修改前: y = ", y);
    std::thread t2(modify_value, std::ref(y));
    t2.join();
    safe_print("  修改後: y = ", y, " (被執行緒修改了)");

    // 字串傳遞
    std::cout << "\n3. 字串傳遞:\n";
    std::string msg = "Hello, Thread!";
    std::thread t3(process_string, msg);
    t3.join();

    // 移動語意
    std::cout << "\n4. 移動語意傳遞:\n";
    std::string data = "Important Data";
    safe_print("  移動前: data = \"", data, "\"");
    std::thread t4([](std::string s) {
        safe_print("  執行緒收到: \"", s, "\"");
    }, std::move(data));
    t4.join();
    safe_print("  移動後: data = \"", data, "\" (已被移動)");

    std::cout << "\n";
}

// ============================================================
// 第五部分:執行緒 ID 與硬體並行度
// ============================================================

void demo_thread_info() {
    std::cout << "========================================\n";
    std::cout << "  執行緒資訊\n";
    std::cout << "========================================\n\n";

    // 主執行緒 ID
    std::cout << "主執行緒 ID: " << std::this_thread::get_id() << "\n";

    // 硬體支援的並行執行緒數
    unsigned int hw_threads = std::thread::hardware_concurrency();
    std::cout << "硬體並行度: " << hw_threads << " 個執行緒\n\n";

    // 各子執行緒回報自己的 ID
    std::cout << "子執行緒 ID:\n";
    std::vector<std::thread> threads;
    for (int i = 0; i < 4; ++i) {
        threads.emplace_back([i]() {
            safe_print("  Thread ", i, " ID: ", std::this_thread::get_id());
        });
    }
    for (auto& t : threads) {
        t.join();
    }

    std::cout << "\n";
}

// ============================================================
// 第六部分:多執行緒實用範例
// ============================================================

void demo_multiple_threads() {
    std::cout << "========================================\n";
    std::cout << "  多執行緒實用範例\n";
    std::cout << "========================================\n\n";

    // 範例 1:平行計算各區段的總和
    std::cout << "1. 平行加總:\n";
    const int total_elements = 1000;
    std::vector<int> data(total_elements);
    for (int i = 0; i < total_elements; ++i) data[i] = i + 1;

    const int num_threads = 4;
    std::vector<long long> partial_sums(num_threads, 0);
    std::vector<std::thread> workers;

    int chunk_size = total_elements / num_threads;

    auto start_time = std::chrono::high_resolution_clock::now();

    for (int t = 0; t < num_threads; ++t) {
        int begin = t * chunk_size;
        int end = (t == num_threads - 1) ? total_elements : begin + chunk_size;

        workers.emplace_back([&data, &partial_sums, t, begin, end]() {
            long long sum = 0;
            for (int i = begin; i < end; ++i) {
                sum += data[i];
            }
            partial_sums[t] = sum;
        });
    }

    for (auto& w : workers) w.join();

    long long total_sum = 0;
    for (auto s : partial_sums) total_sum += s;

    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
        end_time - start_time).count();

    long long expected = static_cast<long long>(total_elements) * (total_elements + 1) / 2;
    std::cout << "  1 到 " << total_elements << " 的總和 = " << total_sum << "\n";
    std::cout << "  預期值 = " << expected << "\n";
    std::cout << "  結果" << (total_sum == expected ? "正確" : "錯誤") << "\n";
    std::cout << "  使用 " << num_threads << " 個執行緒,耗時 " << duration << " μs\n";

    // 範例 2:模擬多個工作者
    std::cout << "\n2. 模擬多個工作者:\n";
    std::vector<std::thread> task_threads;
    std::vector<std::string> task_names = {
        "下載檔案", "壓縮資料", "發送郵件", "備份資料庫"
    };

    for (int i = 0; i < static_cast<int>(task_names.size()); ++i) {
        task_threads.emplace_back([i, &task_names]() {
            safe_print("  [", task_names[i], "] 開始...");
            std::this_thread::sleep_for(
                std::chrono::milliseconds(100 + (i * 50)));
            safe_print("  [", task_names[i], "] 完成 ✓");
        });
    }

    for (auto& t : task_threads) t.join();
    std::cout << "  所有任務完成!\n\n";
}

// ============================================================
// 第七部分:sleep 與 yield
// ============================================================

void demo_sleep_yield() {
    std::cout << "========================================\n";
    std::cout << "  sleep 與 yield\n";
    std::cout << "========================================\n\n";

    // sleep_for:精確等待
    std::cout << "1. sleep_for(精確等待):\n";
    auto start = std::chrono::steady_clock::now();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    auto end = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    std::cout << "  要求 sleep 100ms,實際 " << elapsed << "ms\n";

    // yield:讓出 CPU 時間片
    std::cout << "\n2. yield(讓出 CPU):\n";
    std::cout << "  yield 提示排程器讓其他執行緒先執行\n";
    std::cout << "  通常用在忙等迴圈(busy-wait)中減少 CPU 使用率\n";

    // yield 範例:簡單的 spinlock 概念
    std::atomic<bool> flag{false};
    std::thread setter([&flag]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        flag.store(true);
    });

    int spins = 0;
    while (!flag.load()) {
        std::this_thread::yield();
        ++spins;
    }
    setter.join();
    std::cout << "  等待 flag 設定,自旋 " << spins << " 次\n";

    std::cout << "\n";
}

// ============================================================
// 主程式
// ============================================================

int main() {
    std::cout << "╔══════════════════════════════════════╗\n";
    std::cout << "║  C++17 多執行緒:std::thread          ║\n";
    std::cout << "╚══════════════════════════════════════╝\n\n";

    demo_basic_threads();
    demo_member_function();
    demo_join_detach();
    demo_arguments();
    demo_thread_info();
    demo_multiple_threads();
    demo_sleep_yield();

    std::cout << "=== 程式結束 ===\n";
    return 0;
}

相关文章