Linux多線程編程-哲學家就餐問題詳解與實現(C語言)

在哲學家就餐問題中,假設有五位哲學家圍坐在圓桌前,每位哲學家需要進行思考和進餐兩種活動。他們的思考不需要任何資源,但進餐需要使用兩根筷子(左右兩側各一根)。筷子是共享資源,哲學家們在進行進餐時需要競爭筷子,而且不能出現死鎖情況,即每位哲學家都能在有可能的情況下進餐。

問題示例:

假設有五位哲學家,編號為 0 到 4,圍坐在圓桌周圍,每位哲學家面前有一根筷子。他們的行為可以描述如下:

  1. 思考:哲學家在沒有筷子的時候,思考一段時間。

  2. 進餐:哲學家只有同時拿到左右兩邊的筷子才能進餐,進餐完成后放下筷子繼續思考。

解決方案概述:

  1. 狀態記錄:每位哲學家有三種狀態:思考、饑餓和進餐。
  2. 互斥保護:使用互斥鎖保護對狀態的訪問,確保狀態變化的原子性。
  3. 信號量:使用信號量控制每根筷子的使用,確保每位哲學家能同時拿到左右兩根筷子。

我們用一個數組 state 來記錄每一位哲學家的三個狀態,分別是在進餐狀態、思考狀態、饑餓狀態(正在試圖拿叉子)。

那么,一個哲學家只有在兩個鄰居都沒有進餐時,才可以進入進餐狀態。

第?i?個哲學家的左鄰右舍,則由宏?LEFT?和?RIGHT?定義:

  • LEFT?: ( i + 5 - 1 ) % 5
  • RIGHT?: ( i + 1 ) % 5

比如 i 為 2,則?LEFT?為 1,RIGHT?為 3。

解決步驟:

  1. 定義全局變量和初始化

    • 定義哲學家狀態數組 state[],互斥鎖 pthread_mutex_t mutex 和信號量數組 sem_t chopsticks[]
    • 初始化互斥鎖和每個筷子的信號量。
  2. 哲學家線程函數設計

    • 哲學家線程循環執行思考和進餐過程。
    • 在饑餓狀態時,試圖獲取兩側筷子,獲取成功則進餐,否則等待。
    • 進餐后釋放筷子,繼續思考。
  3. 獲取和釋放筷子的操作

    • 使用互斥鎖保護對狀態數組的修改,確保線程安全。
    • 利用信號量控制筷子的獲取和釋放,避免死鎖和資源爭奪。

示例代碼:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define NUM_PHILOSOPHERS 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2int state[NUM_PHILOSOPHERS];  // 哲學家的狀態數組
pthread_mutex_t mutex;  // 互斥鎖,保護對狀態數組的訪問
sem_t chopsticks[NUM_PHILOSOPHERS];  // 每根筷子的信號量數組void *philosopher(void *arg) {int id = *((int *)arg);while (1) {// 思考printf("哲學家 %d 正在思考。\n", id);sleep(rand() % 3 + 1);  // 模擬思考時間// 進入饑餓狀態printf("哲學家 %d 餓了。\n", id);pthread_mutex_lock(&mutex);state[id] = HUNGRY;printf("哲學家 %d 嘗試拿起筷子。\n", id);test(id);  // 嘗試獲取兩側的筷子pthread_mutex_unlock(&mutex);sem_wait(&chopsticks[id]);  // 獲取筷子,如果沒有則阻塞// 進餐printf("哲學家 %d 開始進餐。\n", id);sleep(rand() % 3 + 1);  // 模擬進餐時間// 放下筷子sem_post(&chopsticks[id]);  // 放下左側筷子sem_post(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);  // 放下右側筷子printf("哲學家 %d 放下筷子,開始思考。\n", id);}pthread_exit(NULL);
}void test(int id) {if (state[id] == HUNGRY && state[(id + NUM_PHILOSOPHERS - 1) % NUM_PHILOSOPHERS] != EATING&& state[(id + 1) % NUM_PHILOSOPHERS] != EATING) {state[id] = EATING;sem_post(&chopsticks[id]);  // 左側筷子sem_post(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);  // 右側筷子}
}int main() {pthread_t philosophers[NUM_PHILOSOPHERS];int ids[NUM_PHILOSOPHERS];// 初始化互斥鎖pthread_mutex_init(&mutex, NULL);// 初始化每根筷子的信號量for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {sem_init(&chopsticks[i], 0, 1);  // 初始值為1,表示筷子可用}// 創建哲學家線程for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {ids[i] = i;pthread_create(&philosophers[i], NULL, philosopher, (void *)&ids[i]);}// 等待線程結束for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {pthread_join(philosophers[i], NULL);}// 清理資源pthread_mutex_destroy(&mutex);for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {sem_destroy(&chopsticks[i]);}return 0;
}

解釋和步驟:

1. 全局變量和初始化
  • state[] 數組:每個元素表示一個哲學家的狀態,初始為思考狀態。
  • pthread_mutex_t mutex:互斥鎖,保護對 state[] 數組的訪問。
  • sem_t chopsticks[NUM_PHILOSOPHERS] 數組:每根筷子對應一個信號量,初始為可用狀態。
2. 哲學家線程函數 philosopher
  • 思考階段:每個哲學家在思考一段時間后進入饑餓狀態。
  • 饑餓階段:哲學家試圖獲取左右兩根筷子,如果成功則進入進餐狀態,否則等待。
  • 進餐階段:成功獲取筷子后進行進餐,一段時間后釋放筷子,回到思考階段。
3. test 函數
  • 檢查能否進入進餐狀態:檢查當前哲學家及其左右鄰居的狀態,如果都是饑餓狀態且兩側筷子可用,則將當前哲學家狀態設置為進餐,并釋放左右兩根筷子。
4. 主函數 main
  • 初始化:初始化互斥鎖和每根筷子的信號量。
  • 創建線程:創建并啟動每個哲學家的線程。
  • 等待線程結束:主線程等待所有哲學家線程執行完畢。
  • 清理資源:銷毀互斥鎖和每根筷子的信號量。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45296.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45296.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45296.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Qt qml詳細介紹

一.基本類型 QML的基本類型包括了很多不同的類型&#xff0c;這些類型可以用于定義用戶界面元素、屬性和信號。以下是一些常用的QML基本類型及其詳細介紹&#xff1a; 數值類型&#xff1a;包括整數類型&#xff08;int、uint、short、ushort等&#xff09;和浮點數類型&#…

c++ :運算符重載函數中的細節

賦值運算符重載與拷貝構造函數 (1)區分初始化時的賦值&#xff08;一般就叫初始化&#xff09;&#xff0c;和非初始化時的賦值&#xff08;一般就叫賦值&#xff09; (2)實驗驗證初始化和賦值時各自對應 避免賦值運算符中的自賦值 (1)自賦值就是Person a; a a; (2)自賦值如…

鞭炮插畫:成都亞恒豐創教育科技有限公司

鞭炮插畫&#xff1a;年味里的絢爛記憶 在歲末年初的溫柔時光里&#xff0c;總有一抹色彩&#xff0c;能瞬間喚醒沉睡的年味——那便是鞭炮插畫中躍動的紅與金&#xff0c;成都亞恒豐創教育科技有限公司 它們不僅僅是紙與墨的交織&#xff0c;更是情感與記憶的橋梁&#xff0c…

自適應手機版大學職業技術學院網站模版源碼系統 帶完整的安裝代碼包以及搭建部署教程

系統概述 隨著智能手機的普及和移動互聯網技術的飛速發展&#xff0c;用戶越來越傾向于通過移動設備訪問網站。對于大學職業技術學院而言&#xff0c;一個能夠自適應各種屏幕尺寸、操作流暢、內容豐富的移動端網站&#xff0c;不僅能夠提升用戶體驗&#xff0c;還能有效擴大學…

最短路之樸素版的dij板子

模板&#xff1a; 注意這個只是單向的雙向的需要在更新一次 #include<bits/stdc.h>using namespace std;typedef long long ll; typedef pair<int, int>PII; const int N2e510; const int MOD 998244353; const int INF0X3F3F3F3F; const int dx[]{-1,1,0,0,-1,…

【Python Tips】將一個列表List元素添加進另一個列表List

一、引言 在處理Python列表數據類型時&#xff0c;有時需要合并兩個列表&#xff0c;下面是幾種列表合并的操作代碼&#xff0c;尤其是對于長列表的高效合并方式&#xff0c;記錄在此。 二、列表合并方式 1. 使用extend方法 extend方法將一個列表中的所有元素添加到另一個列表…

mysql快速精通(三)表關系

主打一個實用 一. 一對多&#xff08;多對一&#xff09;關系 例如班級和學生&#xff0c;這種類型我們一般建兩個表,一方為主表&#xff0c;多方為從表 二. 多對多 例如課程與學生&#xff0c;這種類型我們一般需要建三張表&#xff0c;兩張一方主表&#xff0c;與一張多方從表…

初識影刀:EXCEL根據部門篩選低值易耗品

第一次知道這個辦公自動化的軟件還是在招聘網站上&#xff0c;了解之后發現對于辦公中重復性的工作還是挺有幫助的&#xff0c;特別是那些操作非EXCEL的重復性工作&#xff0c;當然用在EXCEL上更加方便&#xff0c;有些操作比寫VBA便捷。 下面就是一個了解基本操作后&#xff…

[Linux]CentOS軟件的安裝

一、Linux 軟件包管理器 yum 1.Linux安裝軟件的方式 在linux中安裝軟件常用的有三種方式&#xff1a; 源代碼安裝&#xff08;我們還需要進行編譯運行后才可以&#xff0c;很麻煩&#xff09; rpm安裝&#xff08;Linux的安裝包&#xff0c;需要下載一些rpm包&#xff0c;但是…

基于機器學習的鋰離子電池容量估計(MATLAB R2021B)

鋰離子電池已經廣泛應用于電動汽車或混合動力汽車的能源存儲裝置。由于電化學成分的衰退&#xff0c;鋰離子電池隨著使用時間的增加&#xff0c;電池性能不斷退化&#xff0c;導致電池容量和功率發生衰退。電池容量衰退的因素主要有金屬鋰沉積&#xff0c;活性物質分解和電解液…

深度學習DeepLearning多元線性回歸 學習筆記

文章目錄 多維特征變量與術語公式多元線性回歸正規方程法Mean normalizationZ-score normalization設置合適的學習率Feature engineering 多維特征 變量與術語 列屬性xj屬性數n x ? \vec{x} x (i)行向量某個值 x ? j i \vec{x}_j^i x ji?上行下列均值μ標準化標準差σsigm…

SpringMVC 中常用注解

在 SpringMVC 框架的開發中&#xff0c;注解的合理運用能夠極大地提高開發效率和代碼的可維護性。今天&#xff0c;讓我們一起來總結一下 SpringMVC 中一些常用的注解及其用法。 一、Controller 注解 Controller 用于標識一個控制器類&#xff0c;該類中的方法用于處理用戶的請…

ArduPilot開源代碼之AP_AHRS_Backend

ArduPilot開源代碼之AP_AHRS_Backend 1. 源由2. 類繼承關系3. 框架設計2.1 構造函數和析構函數2.2 不可復制2.3 嵌套結構和枚舉2.4 虛方法2.5 靜態方法2.6 實用方法2.7 純虛方法2.8 條件編譯 3. 虛方法設計3.1 初始化3.1.1 構造函數3.1.2 析構函數3.1.3 AP_AHRS_Backend::init …

Chromium CI/CD 之Jenkins實用指南2024-如何創建新節點(三)

1. 前言 在前一篇《Jenkins實用指南2024-系統基本配置&#xff08;二&#xff09;》中&#xff0c;我們詳細介紹了如何對Jenkins進行基本配置&#xff0c;包括系統設置、安全配置、插件管理以及創建第一個Job。通過這些配置&#xff0c;您的Jenkins環境已經具備了基本的功能和…

基于pyqt5實現xlsx選擇器應用程序

環境搭建 基于python3.12pyqt5 pip3 install PyQt5 pip3 install pyinstallerpyinstaller --onefile --windowed test.py代碼 新建main.py import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLineEdit, QFileDial…

leetcode 665.非遞減數列

1.題目要求: 給你一個長度為 n 的整數數組 nums &#xff0c;請你判斷在 最多 改變 1 個元素的情況下&#xff0c;該數組能否變成一個非遞減數列。我們是這樣定義一個非遞減數列的&#xff1a; 對于數組中任意的 i (0 < i < n-2)&#xff0c;總滿足 nums[i] < nums[i…

Java 設計模式系列:外觀模式

簡介 外觀模式&#xff08;Facade Pattern&#xff09;是一種設計模式&#xff0c;又名門面模式&#xff0c;是一種通過為多個復雜的子系統提供一個一致的接口&#xff0c;而使這些子系統更加容易被訪問的模式。該模式對外有一個統一接口&#xff0c;外部應用程序不用關心內部…

Android中RecyclerView使用詳解(一)

目錄 概述優點列表布局RecyclerView一、創建RecyclerView并且在布局中綁定二、實現RecyclerView單個item的布局三、給RecyclerView寫一個對應的適配器Adapter1.創建自定義的ViewHolder2.繼承Adapter&#xff0c;泛型使用我們自定義的ViewHolder3.重寫Adapter的三個方法onCreate…

線程安全(二)synchronized 的底層實現原理、鎖升級、對象的內存結構

目錄 一、基礎使用1.1 不加鎖的代碼實現1.2 加鎖的代碼實現二、實現原理2.1 synchronized 簡介2.2 對象監控器(Monitor)2.3 加鎖過程第一步:判斷 Owner 指向第二步:進入 EntryList 阻塞第三步:主動進入 WaitSet 等待三、鎖升級3.1 對象的內存結構3.2 Mark Word 對象頭3.3 …

MySQL sql_safe_updates參數

sql_safe_updates 是 MySQL 中的一個系統變量&#xff0c;用于控制 MySQL 服務器是否允許在沒有使用 KEY 或 LIMIT 子句的 UPDATE 或 DELETE 語句上執行更新或刪除操作。當這個變量被設置為 ON 時&#xff0c;MySQL 會拒絕那些可能影響到表中大量行的 UPDATE 或 DELETE 語句&am…