1. 前驱 - 后继关系的进程控制(P92)

问题描述:多个进程需按特定顺序执行

解决方案

  • 每个前驱进程对应一个信号量,初始化为 0。
  • 前驱进程完成后执行 V 操作,通知后继进程。
  • 后继进程在开始前通过 P 操作等待所有前驱完成。

代码:(std=c++23 后同)

#include <iostream>
#include <semaphore>
#include <thread>
 
std::binary_semaphore a(0), b(0), c(0), d(0), e(0), f(0), g(0), h(0);
 
void S1() {
    std::cout << "S1\n";
    a.release();
    b.release();
}
void S2() {
    a.acquire();
    std::cout << "S2\n";
    c.release();
    d.release();
}
void S3() {
    b.acquire();
    std::cout << "S3\n";
    e.release();
}
void S4() {
    c.acquire();
    std::cout << "S4\n";
    f.release();
}
void S5() {
    d.acquire();
    std::cout << "S5\n";
    g.release();
}
void S6() {
    f.acquire();
    g.acquire();
    e.acquire();
    std::cout << "S6\n";
}
 
int main() {
    std::jthread j1(S1), j2(S2), j3(S3), j4(S4), j5(S5), j6(S6);
    return 0;
}

2. 父亲、儿子、女儿的同步(P113)

问题描述:父亲向盘子放水果(苹果或橘子),儿子取桔子,女儿取橘苹果,盘子一次只能放一个水果。

解决方案

  • 使用三个信号量:
    • empty:表示盘子是否为空(初始为 1)。
    • appleorange:表示是否有对应水果可拿(初始为 0)。
  • 父亲放水果前等待盘子为空,放入后通知对应孩子。
  • 孩子取水果后释放盘子。

代码

#include <cstddef>
#include <iostream>
#include <semaphore>
#include <thread>
 
std::binary_semaphore empty(1), apple(0), orange(0);
 
void father() {
    for (size_t i = 0; i < 10; i++) {
        bool is_apple = (i % 2 == 0);
        empty.acquire();
        if (is_apple) {
            std::cout << "Father: produced apple\n";
            apple.release();
        } else {
            std::cout << "Father: produced orange\n";
            orange.release();
        }
    }
}
 
void son() {
    for (size_t i = 0; i < 5; i++) {
        orange.acquire();
        std::cout << "Son: comsume orange\n";
        empty.release();
    }
}
 
void daughter() {
    for (size_t i = 0; i < 5; i++) {
        apple.acquire();
        std::cout << "Daughter: comsume apple\n";
        empty.release();
    }
}
 
int main() {
    std::jthread j1(father), j2(son), j3(daughter);
    return 0;
}

3. 拥挤路段的控制(P119)

问题描述:限制路段同时通过的车辆数,左右均是两车道有来车,中间只能允许一辆车单向通行

解决方案

  • 使用三个信号量,lmut 和 rmut 保护各自的计数器,tmut 是桥的通行权
  • 第一个能够上桥的抢过桥的通行权,然后增加该方向的计数。如果后来的同方向的发现计数大于 0 则可以跟着上桥
  • 下桥的时候一样用 lmut 和 rmut 保护,最后一个下桥的恢复桥的通行权

代码

#include <chrono>
#include <iostream>
#include <semaphore>
#include <thread>
 
int lc = 0, rc = 0;
std::binary_semaphore lmut(1), rmut(1), tmut(1);
 
void l_vehicle() {
    // 1. 保证现在是这个方向了
    lmut.acquire();
    if (lc == 0) { tmut.acquire();} // 如果是第一个,就抢过桥的使用权
    lc++;
    lmut.release();
    // 2. 开始行动
    std::cout << "go from left\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    // 3. 完成释放
    lmut.acquire();
    lc--;
    if (lc == 0) { tmut.release(); } // 最后一辆车释放桥的使用权
    lmut.release();
}
 
void r_vehicle() {
    rmut.acquire();
    if (rc == 0) { tmut.acquire(); }
    rc++;
    rmut.release();
    std::cout << "go from right\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    rmut.acquire();
    rc--;
    if (rc == 0) { tmut.release(); }
    rmut.release();
}
 
int main() {
    std::jthread j[] = {
        std::jthread(r_vehicle), std::jthread(l_vehicle),
        std::jthread(l_vehicle), std::jthread(l_vehicle),
        std::jthread(r_vehicle), std::jthread(r_vehicle),
        std::jthread(r_vehicle), std::jthread(l_vehicle),
        std::jthread(r_vehicle),
    };
    return 0;
}