iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

以下為使用 POSIX 線程(pthread)的死結範例 deadlock.c

/**
 * A pthread program illustrating deadlock.
 *
 * Usage:
 *	    gcc deadlock.c -lpthread
 *	    ./a.out
 *
 * Figure 7.4
 *
 * @author Gagne, Galvin, Silberschatz
 * Operating System Concepts  - Ninth Edition
 * Copyright John Wiley & Sons - 2013.
 */

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t first_mutex;
pthread_mutex_t second_mutex;

void *do_work_one(void *param); 
void *do_work_two(void *param); 

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2; /* the thread identifiers */
    pthread_attr_t attr; /* set of attributes for the thread */

    /* get the default attributes */
    pthread_attr_init(&attr);

    /* create the mutex locks */
    pthread_mutex_init(&first_mutex,NULL);
    pthread_mutex_init(&second_mutex,NULL);

    /* create the threads */
    pthread_create(&tid1,&attr,do_work_one,NULL);
    pthread_create(&tid2,&attr,do_work_two,NULL);

    /* now wait for the thread to exit */
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    printf("Parent DONE\n");

    /* destroy the mutex before exiting */
    pthread_mutex_destroy(&first_mutex);
    pthread_mutex_destroy(&second_mutex);
}

/**
 * The first thread worker
 */
void *do_work_one(void *param) 
{
	pthread_mutex_lock(&first_mutex);
    printf("Worker 1 has acquired first mutex\n");
	pthread_mutex_lock(&second_mutex);
    printf("Worker 1 has acquired second mutex\n");

	/**
	 * Do some work
	 */

    printf("Worker 1 is whistling .....\n");

	pthread_mutex_unlock(&second_mutex); 
	pthread_mutex_unlock(&first_mutex); 

    printf("worker 1 done\n");

	pthread_exit(0);
}

/**
 * The second thread worker
 */
void *do_work_two(void *param)
{
        pthread_mutex_lock(&second_mutex);
        printf("Worker 2 has acquired second mutex\n");
        pthread_mutex_lock(&first_mutex);
        printf("Worker 2 has acquired first mutex\n");

        /**
         * Do some work
         *
         */

        printf("Worker 2 is whistling .....\n");

        pthread_mutex_unlock(&first_mutex);
        pthread_mutex_unlock(&second_mutex);

        pthread_exit(0);
}

這個程式碼是一個使用 POSIX 線程(pthread)的 C 語言程式,用來演示死鎖(deadlock)的情況。讓我們請AI詳細解析這個程式:

  1. 程式結構:

    • 包含必要的頭文件:pthread.h(線程相關)、stdio.h(標準輸入輸出)和 unistd.h(POSIX 操作系統 API)。
    • 定義了兩個全局的互斥鎖(mutex):first_mutex 和 second_mutex。
    • 聲明了兩個工作函數:do_work_one 和 do_work_two。
  2. main 函數:

    • 創建兩個線程標識符(tid1 和 tid2)和一個線程屬性結構(attr)。
    • 初始化線程屬性和兩個互斥鎖。
    • 創建兩個線程,分別執行 do_work_one 和 do_work_two 函數。
    • 等待兩個線程結束(使用 pthread_join)。
    • 最後,銷毀互斥鎖並結束程式。
  3. do_work_one 函數:

    • 首先嘗試鎖定 first_mutex。
    • 然後嘗試鎖定 second_mutex。
    • 執行一些工作(這裡只是打印一條消息)。
    • 按照相反的順序釋放互斥鎖。
  4. do_work_two 函數:

    • 首先嘗試鎖定 second_mutex。
    • 然後嘗試鎖定 first_mutex。
    • 執行一些工作(同樣只是打印一條消息)。
    • 按照相反的順序釋放互斥鎖。
  5. 死鎖情況:
    這個程式演示了一個典型的死鎖場景:

    • 如果 do_work_one 獲得了 first_mutex,同時 do_work_two 獲得了 second_mutex,
    • 然後 do_work_one 嘗試獲取 second_mutex(已被 do_work_two 鎖定),
    • 而 do_work_two 嘗試獲取 first_mutex(已被 do_work_one 鎖定),
    • 這樣兩個線程都在等待對方釋放資源,形成死鎖。
  6. 編譯和運行:

    • 使用 gcc 編譯:gcc deadlock.c -lpthread
    • 運行:./a.out
  7. 潛在問題:

    • 這個程式可能不會每次運行都產生死鎖,因為線程的執行順序是不確定的。
    • 在某些運行中,一個線程可能會在另一個線程開始之前完成其工作,從而避免死鎖。
  8. 教學目的:

    • 這個程式的主要目的是展示如何創建可能導致死鎖的情況,以及多線程程式中資源競爭的問題。
    • 它強調了在設計多線程應用時需要小心處理資源分配和鎖定順序的重要性。

結果:
https://ithelp.ithome.com.tw/upload/images/20241010/20168766QwM2T6QDj5.png

這個輸出表明:

  1. 程式開始執行。

  2. Worker 2(do_work_two 函數)首先獲得了 second_mutex。

  3. Worker 1(do_work_one 函數)接著獲得了 first_mutex。

  4. 程式在這裡停止了,沒有進一步的輸出。

這個情況正好演示了一個典型的死鎖:

  • Worker 2 持有 second_mutex,並且正在等待獲取 first_mutex。
  • Worker 1 持有 first_mutex,並且正在等待獲取 second_mutex。

兩個線程都在等待對方釋放自己需要的資源,但由於都不願意釋放自己持有的資源,導致程式陷入了死鎖狀態。

這個結果清楚地展示了程式中設計的死鎖情況:

  1. do_work_two 函數首先鎖定了 second_mutex。
  2. do_work_one 函數接著鎖定了 first_mutex。
  3. 現在,do_work_two 正在等待 first_mutex(被 do_work_one 持有)。
  4. 同時,do_work_one 正在等待 second_mutex(被 do_work_two 持有)。

這個死鎖情況的關鍵在於資源(互斥鎖)的請求順序:

  • do_work_one 的順序是:first_mutex -> second_mutex
  • do_work_two 的順序是:second_mutex -> first_mutex

這種交叉的資源請求順序是造成死鎖的常見原因。

解決這種死鎖的方法包括:

  1. 保持一致的資源請求順序(例如,總是先請求 first_mutex,再請求 second_mutex)。
  2. 使用超時機制,在等待一段時間後放棄請求並釋放已持有的資源。
  3. 使用更高級的同步機制,如讀寫鎖或條件變量。
  4. 實現資源分配的階級制度,避免循環等待。

這個輸出很好地展示了為什麼在設計多線程程式時,正確處理資源分配和互斥是如此重要。它也說明了為什麼在實際應用中,我們需要謹慎設計並仔細測試涉及多個鎖的代碼。


上一篇
ch6-哲學家進餐問題(Dining Philosophers Problem)
下一篇
ch7-死結,忽略就對了
系列文
十年後重讀作業系統恐龍本30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言