windows C++-避免死锁(下)

ops/2024/10/15 22:13:08/

使用 join 防止死锁

下面介绍了如何使用消息缓冲区和消息传递函数来消除死锁的可能性。

为了将该示例与上一示例相关联,philosopher 类通过使用 concurrency::unbounded_buffer 对象和 join 对象来替换每个 critical_section 对象。 join 对象充当为哲学家提供筷子的仲裁程序。

此示例使用 unbounded_buffer 类,因为当目标从 unbounded_buffer 对象收到消息时,会将该消息从消息队列中移除。 这样,包含一条消息的 unbounded_buffer 对象就可以指示有筷子可用。 不包含消息的 unbounded_buffer 对象会指示筷子正在使用中。

此示例使用非贪婪 join 对象,因为非贪婪联接仅当两个 unbounded_buffer 对象都包含消息时,才允许每个 philosopher 对象访问两根筷子。 贪婪联接不会阻止死锁,因为贪婪联接会在消息可用后立即接受消息。 如果所有贪婪 join 对象都收到其中一条消息,但一直要等待另一条消息变为可用,则会发生死锁。

在此示例中防止死锁

1. 从示例中移除以下代码。

// A shared array of critical sections. Each critical section 
// guards access to a single chopstick.
critical_section locks[philosopher_count];

2. 将 philosopher 类的 _left 和 _right 数据成员的类型更改为 unbounded_buffer。

// Message buffer for the left chopstick.
unbounded_buffer<chopstick>& _left;
// Message buffer for the right chopstick.
unbounded_buffer<chopstick>& _right;

3. 修改 philosopher 构造函数以将 unbounded_buffer 对象作为其参数。

// Gains access to the chopsticks.
vector<int> pickup_chopsticks()
{// Create a non-greedy join object and link it to the left and right // chopstick.join<chopstick, non_greedy> j(2);_left.link_target(&j);_right.link_target(&j);// Receive from the join object. This resolves the deadlock situation// because a non-greedy join removes the messages only when a message// is available from each of its sources.return receive(&j);
}

4. 修改 putdown_chopsticks 方法,以通过向两根筷子的消息缓冲区发送消息来释放对筷子的访问。 

// Releases the chopsticks for others.
void putdown_chopsticks(int left, int right)
{// Add the values of the messages back to the message queue.asend(&_left, left);asend(&_right, right);
}

5. 修改 run 方法以包含 pickup_chopsticks 方法的结果,并将这些结果传递给 putdown_chopsticks 方法。

// Performs the main logic of the dining philosopher algorithm.
void run()
{// Repeat the thinks/eat cycle a set number of times.for (int n = 0; n < eat_count; ++n){think();vector<int> v = pickup_chopsticks(); eat();send(_times_eaten, n+1);putdown_chopsticks(v[0], v[1]);}done();
}

6. 将 wmain 函数中 chopsticks 变量的声明修改为每个包含一条消息的 unbounded_buffer 对象数组。

// Create an array of message buffers to hold the chopsticks.
array<unbounded_buffer<chopstick>, philosopher_count> chopsticks;// Send a value to each message buffer in the array.
// The value of the message is not important. A buffer that contains
// any message indicates that the chopstick is available.
for_each (begin(chopsticks), end(chopsticks), [](unbounded_buffer<chopstick>& c) {send(c, 1);
});

完整代码

// philosophers-join.cpp
// compile with: /EHsc
#include <agents.h>
#include <string>
#include <array>
#include <iostream>
#include <algorithm>
#include <random>using namespace concurrency;
using namespace std;// Defines a single chopstick.
typedef int chopstick;// The total number of philosophers.
const int philosopher_count = 5;// The number of times each philosopher should eat.
const int eat_count = 50;// Implements the logic for a single dining philosopher.
class philosopher : public agent 
{
public:explicit philosopher(unbounded_buffer<chopstick>& left, unbounded_buffer<chopstick>& right, const wstring& name): _left(left), _right(right), _name(name), _random_generator(42){send(_times_eaten, 0);}// Retrieves the number of times the philosopher has eaten.int times_eaten(){return receive(_times_eaten);}// Retrieves the name of the philosopher.wstring name() const{return _name;}protected:// Performs the main logic of the dining philosopher algorithm.void run(){// Repeat the thinks/eat cycle a set number of times.for (int n = 0; n < eat_count; ++n){think();vector<int> v = pickup_chopsticks(); eat();send(_times_eaten, n+1);putdown_chopsticks(v[0], v[1]);}done();}// Gains access to the chopsticks.vector<int> pickup_chopsticks(){// Create a non-greedy join object and link it to the left and right // chopstick.join<chopstick, non_greedy> j(2);_left.link_target(&j);_right.link_target(&j);// Receive from the join object. This resolves the deadlock situation// because a non-greedy join removes the messages only when a message// is available from each of its sources.return receive(&j);}// Releases the chopsticks for others.void putdown_chopsticks(int left, int right){// Add the values of the messages back to the message queue.asend(&_left, left);asend(&_right, right);}// Simulates thinking for a brief period of time.void think(){random_wait(100);}// Simulates eating for a brief period of time.void eat(){      random_wait(100);      }private:// Yields the current context for a random period of time.void random_wait(unsigned int max){concurrency::wait(_random_generator()%max);}private:// Message buffer for the left chopstick.unbounded_buffer<chopstick>& _left;// Message buffer for the right chopstick.unbounded_buffer<chopstick>& _right;// The name of the philosopher.wstring _name;// Stores the number of times the philosopher has eaten.overwrite_buffer<int> _times_eaten;// A random number generator.mt19937 _random_generator;
};int wmain()
{// Create an array of message buffers to hold the chopsticks.array<unbounded_buffer<chopstick>, philosopher_count> chopsticks;// Send a value to each message buffer in the array.// The value of the message is not important. A buffer that contains// any message indicates that the chopstick is available.for_each (begin(chopsticks), end(chopsticks), [](unbounded_buffer<chopstick>& c) {send(c, 1);});// Create an array of philosophers. Each pair of neighboring // philosophers shares one of the chopsticks.array<philosopher, philosopher_count> philosophers = {philosopher(chopsticks[0], chopsticks[1], L"aristotle"),philosopher(chopsticks[1], chopsticks[2], L"descartes"),philosopher(chopsticks[2], chopsticks[3], L"hobbes"),philosopher(chopsticks[3], chopsticks[4], L"socrates"),philosopher(chopsticks[4], chopsticks[0], L"plato"),};// Begin the simulation.for_each (begin(philosophers), end(philosophers), [](philosopher& p) {p.start();});// Wait for each philosopher to finish and print his name and the number// of times he has eaten.for_each (begin(philosophers), end(philosophers), [](philosopher& p) {agent::wait(&p);wcout << p.name() << L" ate " << p.times_eaten() << L" times." << endl;});
}

编译代码

复制示例代码,并将它粘贴到 Visual Studio 项目中,或粘贴到名为 philosophers-join.cpp 的文件中,再在 Visual Studio 命令提示符窗口中运行以下命令。

cl.exe /EHsc philosophers-join.cpp


http://www.ppmy.cn/ops/126135.html

相关文章

mac下docker的详细安装和配置

linux的docker安装请参考&#xff1a;linux下docker详细安装&#xff0c;在Mac上安装Docker也相对简单&#xff0c;以下是详细步骤&#xff1a; 1. 系统要求 确保你的Mac满足以下要求&#xff1a; macOS 10.14&#xff08;Mojave&#xff09;或更高版本至少4GB RAM 2. 下载…

img标签的title和alt的区别,png、jpg、gif、格式区别

img标签的title和alt有什么区别&#xff1f; 区别一&#xff1a; title&#xff1a;鼠标移入到图片显示的值 alt&#xff1a;图片无法加载时显示的值 区别二&#xff1a; 在seo的层面上&#xff0c;蜘蛛抓取不到图片的内容&#xff0c;所以前端在写img标签的时候为了增加seo效果…

Java 多线程(三)—— 死锁

死锁的产生 我们先从简单的死锁最后到难一些的死锁问题开始展开讨论。 首先一个线程&#xff0c;一把锁&#xff0c;因为多次加锁而导致死锁问题&#xff0c;由于Java 的synchronized 实现了可重入锁&#xff0c;因此这个死锁问题就不存在了&#xff0c;意味着当一个线程拥有…

测试用例的编写

1.基本概念&#xff1a; 编写测试用例是确保代码质量和正确性的重要环节&#xff0c;尤其是在软件开发和维护过程中。测试用例通常用于验证功能是否符合预期&#xff0c;并及时发现潜在的错误或漏洞。 2.常见的测试用例编写方法&#xff1a; 等价划分法&#xff0c;边界值法&a…

线性回归损失函数的推导

要推导损失函数公式 ℓ ( θ ) 1 2 n ( y ^ − y ) ⊤ ( y ^ − y ) \ell(\boldsymbol{\theta}) \frac{1}{2n}(\hat{\boldsymbol{y}} - \boldsymbol{y})^\top(\hat{\boldsymbol{y}} - \boldsymbol{y}) ℓ(θ)2n1​(y^​−y)⊤(y^​−y)&#xff0c;我们可以从几个基础概念开…

安装R和RStudio:开始你的数据分析之旅

数据分析是当今世界中一个非常热门的领域&#xff0c;而R语言是进行数据分析的强大工具之一。R是一种编程语言和软件环境&#xff0c;用于统计计算和图形表示。RStudio是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;它为R语言提供了一个更加友好和高效的工作环境。…

Python | Leetcode Python题解之第474题一和零

题目&#xff1a; 题解&#xff1a; class Solution:def findMaxForm(self, strs: List[str], m: int, n: int) -> int:count10 []for s in strs:count10.append([0,0])for c in s:if c 0: count10[-1][0]1else: count10[-1][1]1dp [[0]*(n1) for _ in range(m1)]for i …

Go 语言中的格式化占位符

在 Go 语言中&#xff0c;fmt 包提供了大量的格式化占位符&#xff0c;用于格式化输出不同类型的数据。选择合适的占位符&#xff0c;可以确保输出的内容格式正确、清晰易懂。 常见的占位符&#xff1a; 基本类型 %v&#xff1a;按值的默认格式输出。适用于任何类型。%v&…