追烽少年x头像
关注

C++11多线程模型

一、读者-写者问题

核心:允许多个读者并发进入,写者独占。经典"读者优先"解法用一个 read_count + 两把锁实现。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>

class ReadWriteLock {
    std::mutex mtx_;
    std::condition_variable cv_;
    int readers_ = 0;          // 当前正在读的读者数
    bool writer_active_ = false; // 是否有写者在写
public:
    void read_lock() {
        std::unique_lock<std::mutex> lk(mtx_);
        cv_.wait(lk, [this]{ return !writer_active_; }); // 无写者才能进
        ++readers_;
    }
    void read_unlock() {
        std::unique_lock<std::mutex> lk(mtx_);
        if (--readers_ == 0) cv_.notify_all();  // 最后一个读者离开,唤醒写者
    }
    void write_lock() {
        std::unique_lock<std::mutex> lk(mtx_);
        cv_.wait(lk, [this]{ return !writer_active_ && readers_ == 0; });
        writer_active_ = true;
    }
    void write_unlock() {
        std::unique_lock<std::mutex> lk(mtx_);
        writer_active_ = false;
        cv_.notify_all();
    }
};

ReadWriteLock rw;
int shared_data = 0;

void reader(int id) {
    for (int i = 0; i < 3; ++i) {
        rw.read_lock();
        std::cout << "Reader " << id << " reads: " << shared_data << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        rw.read_unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void writer(int id) {
    for (int i = 0; i < 2; ++i) {
        rw.write_lock();
        ++shared_data;
        std::cout << "Writer " << id << " writes: " << shared_data << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        rw.write_unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
}

int main() {
    std::vector<std::thread> ts;
    for (int i = 1; i <= 3; ++i) ts.emplace_back(reader, i);
    for (int i = 1; i <= 2; ++i) ts.emplace_back(writer, i);
    for (auto& t : ts) t.join();
    return 0;
}

二、睡眠理发师问题

理发店有一个理发师、N 把等候椅。无顾客时理发师睡觉;顾客来时若椅子满则离开,否则唤醒理发师

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

constexpr int MAX_CHAIRS = 5;   // 等候椅数量

class BarberShop {
    std::mutex mtx_;
    std::condition_variable cv_customer_, cv_barber_;
    std::queue<int> waiting_;   // 等候的顾客 id
    bool barber_sleeping_ = true;
    bool customer_ready_ = false;
    bool haircut_done_ = false;
    bool shop_open_ = true;
public:
    // 顾客线程
    bool customer(int id) {
        std::unique_lock<std::mutex> lk(mtx_);
        if (waiting_.size() >= MAX_CHAIRS) {
            std::cout << "Customer " << id << " leaves (no chair)\n";
            return false;
        }
        waiting_.push(id);
        if (barber_sleeping_) {
            barber_sleeping_ = false;
            cv_barber_.notify_one();        // 叫醒理发师
        }
        // 等待轮到自己理发
        cv_customer_.wait(lk, [&]{ return !shop_open_ || waiting_.front() == id; });
        if (!shop_open_) return false;
        waiting_.pop();
        customer_ready_ = true;
        cv_barber_.notify_one();
        // 等待理发完成
        cv_customer_.wait(lk, [&]{ return haircut_done_; });
        haircut_done_ = false;
        std::cout << "Customer " << id << " done\n";
        return true;
    }

    // 理发师线程
    void barber() {
        while (true) {
            std::unique_lock<std::mutex> lk(mtx_);
            cv_barber_.wait(lk, [&]{ return !shop_open_ || customer_ready_ || !waiting_.empty(); });
            if (!shop_open_ && waiting_.empty() && !customer_ready_) return;

            if (!customer_ready_ && !waiting_.empty()) {
                cv_customer_.notify_all();   // 让队首顾客进入
            }
            cv_barber_.wait(lk, [&]{ return customer_ready_; });
            lk.unlock();

            std::cout << "Barber is cutting hair...\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(200));

            lk.lock();
            customer_ready_ = false;
            haircut_done_ = true;
            cv_customer_.notify_all();
            if (waiting_.empty()) barber_sleeping_ = true;
        }
    }

    void close() {
        std::unique_lock<std::mutex> lk(mtx_);
        shop_open_ = false;
        cv_barber_.notify_all();
        cv_customer_.notify_all();
    }
};

BarberShop shop;

void barber_thread() { shop.barber(); }
void customer_thread(int id) { shop.customer(id); }

int main() {
    std::thread b(barber_thread);
    std::vector<std::thread> cs;
    for (int i = 1; i <= 8; ++i) {
        cs.emplace_back(customer_thread, i);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    for (auto& t : cs) t.join();
    shop.close();
    b.join();
    return 0;
}

三、吸烟者问题

代理每次随机放两种原料在桌上,拥有第三种原料的吸烟者才能卷烟并抽完,再通知代理继续

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <cstdlib>

// 三种原料:0=烟草, 1=纸, 2=火柴
// 吸烟者 i 拥有原料 i,缺少其他两种

class Smokers {
    std::mutex mtx_;
    std::condition_variable cv_smoker_[3], cv_agent_;
    bool on_table_[3] = {false, false, false};
    bool agent_waiting_ = true;
public:
    void agent() {
        for (int round = 0; round < 5; ++round) {
            std::unique_lock<std::mutex> lk(mtx_);
            int a = std::rand() % 3;
            int b = (a + 1 + std::rand() % 2) % 3;   // 取不同于 a 的另一种
            on_table_[a] = on_table_[b] = true;
            std::cout << "Agent puts ingredients " << a << " & " << b << "\n";
            cv_smoker_[a].notify_all();
            cv_smoker_[b].notify_all();
            cv_agent_.wait(lk, [&]{ return agent_waiting_; });
            agent_waiting_ = false;
            lk.unlock();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }

    void smoker(int id) {
        while (true) {
            std::unique_lock<std::mutex> lk(mtx_);
            cv_smoker_[id].wait(lk, [&]{ return on_table_[(id+1)%3] && on_table_[(id+2)%3]; });
            on_table_[0] = on_table_[1] = on_table_[2] = false;
            std::cout << "Smoker " << id << " rolls & smokes\n";
            lk.unlock();
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            lk.lock();
            agent_waiting_ = true;
            cv_agent_.notify_one();
        }
    }
};

int main() {
    std::srand(std::time(nullptr));
    Smokers s;
    std::thread agt(&Smokers::agent, &s);
    std::vector<std::thread> sms;
    for (int i = 0; i < 3; ++i) sms.emplace_back(&Smokers::smoker, &s, i);
    agt.join();
    // 演示用,实际中吸烟者线程会一直循环
    return 0;
}

四、屏障同步(Barrier)

用于"多线程各自完成一段工作后,等所有线程都到达某点再继续"。C++20 才有 std::barrier,C++11 需要自己实现:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>

class Barrier {
    std::mutex mtx_;
    std::condition_variable cv_;
    int count_, total_;
    int generation_ = 0;
public:
    explicit Barrier(int n) : count_(n), total_(n) {}
    void wait() {
        std::unique_lock<std::mutex> lk(mtx_);
        int gen = generation_;
        if (--count_ == 0) {
            count_ = total_;
            ++generation_;
            cv_.notify_all();
        } else {
            cv_.wait(lk, [&]{ return generation_ != gen; });
        }
    }
};

Barrier barrier(4);

void worker(int id) {
    for (int round = 0; round < 3; ++round) {
        std::cout << "Worker " << id << " phase " << round << " done\n";
        barrier.wait();  // 所有线程都到齐才进入下一阶段
    }
}

int main() {
    std::vector<std::thread> ts;
    for (int i = 0; i < 4; ++i) ts.emplace_back(worker, i);
    for (auto& t : ts) t.join();
    return 0;
}

小结

问题核心难点常用原语
读者-写者读写互斥、读读并发、避免写者饥饿mutex + condition_variable + 计数器
睡眠理发师有限等待队列、唤醒机制mutex + 两个 condition_variable + 队列
吸烟者资源组合匹配、代理与吸烟者的握手mutex + 多个 condition_variable
屏障等待全员到齐再继续mutex + condition_variable + 代际计数

其中读者-写者和睡眠理发师是 Tanenbaum 书中明确讲到的两大经典 IPC 问题83.136.203,吸烟者和屏障属于常见补充。这些模型也对应后续章节讨论的"进程通信"与"死锁"章节——哲学家就餐用于演示死锁避免,睡眠理发师用于演示资源分配同步,读者-写者用于演示并发控制策略,建议对照原书一起看效果更好。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_45144862/article/details/166950351

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--