簡體   English   中英

pthread_attr_setinheritsched 究竟做了什么?

[英]What does pthread_attr_setinheritsched exactly do?

我正在解決一個學校問題。 我不想要這個問題的答案,但我確實有一個問題。

我正在處理 POSIX 線程,我有 4 個任務,我需要使用優先級在任務 1 和 2 之前運行任務 3 和 4。

在他沒有放假之前,我的老師給了我一堆示例代碼,我正在努力弄清楚。 特別是行pthread_attr_setinheritsched(&tattr1,PTHREAD_EXPLICIT_SCHED); . 我不知道它做了什么。 我知道如果我刪除它,所有線程都會運行而不檢查它們的優先級。 如果我不刪除它,線程根本不會運行。 所以調試起來有點挑剔。

我在下面提供了完整的代碼(任務除外)。 有人可以解釋那條線的確切作用嗎?

int main() {
    // everything on core 0
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(0, &mask);
    sched_setaffinity(0, sizeof(mask), &mask);
    
    // attr1
    pthread_attr_t tattr1;
    struct sched_param param1;
    pthread_attr_init(&tattr1);
    pthread_attr_setschedpolicy(&tattr1,SCHED_RR);
    pthread_attr_getschedparam(&tattr1,&param1);
    param1.sched_priority=10;
    pthread_attr_setinheritsched(&tattr1,PTHREAD_EXPLICIT_SCHED); // <---- THIS LINE
    pthread_attr_setschedparam(&tattr1,&param1);
    
    // attr2
    pthread_attr_t tattr2;
    struct sched_param param2;
    pthread_attr_init(&tattr2);
    pthread_attr_setschedpolicy(&tattr2,SCHED_RR);
    pthread_attr_getschedparam(&tattr2,&param2);
    param2.sched_priority=0;
    pthread_attr_setinheritsched(&tattr2,PTHREAD_EXPLICIT_SCHED); // <---- THIS LINE
    pthread_attr_setschedparam(&tattr2,&param2);
    
    // spin up threads
    pthread_t tid_1;
    pthread_create(&tid_1, &tattr1, taskOne, NULL);
    pthread_t tid_2;
    pthread_create(&tid_2, &tattr1, taskTwo, NULL);
    pthread_t tid_3;
    pthread_create(&tid_3, &tattr2, taskThree, NULL);
    pthread_t tid_4;
    pthread_create(&tid_4, &tattr2, taskFour, NULL);
    
    pthread_join(tid_1, NULL);
    pthread_join(tid_2, NULL);
    pthread_join(tid_3, NULL);
    pthread_join(tid_4, NULL);
}

歡迎來到 OS 線程調度的世界。

首先,一個線程。=一個線程。

也就是說這段代碼是不可移植的; 具體來說,如果您在一個支持 POSIX 線程的操作系統上編譯此代碼,它不一定在另一個操作系統上運行相同。

例如,如果我在 Fedora 35 上運行此代碼(假設“任務”是簡單的printf語句),那么我只會得到taskOnetaskTwo ,就好像我在我的 macOS(基於 BSD 的內核)上編譯它一樣,我獲取所有“任務”output。

這是因為線程調度(您的代碼正在處理的內容)特定於操作系統的,而 POSIX 標准甚至聲明此代碼不必產生相同的結果。


有了這個,我將直接回答您的問題,而不是對各種線程調度進行抨擊(過去我也過幾個關於 POSIX 線程調度的問題)。

pthread_attr_setinheritsched function 按照它說的做; 它設置線程的調度 inheritance,這基本上意味着如果你從另一個線程創建一個線程,你可以讓那個“子”線程“繼承”“父”線程的調度,或者你可以明確地(通過PTHREAD_EXPLICIT_SCHED )設置它調度。

如果您有一個線程具有特定的調度策略(例如SCHED_FIFO ),您不想為從該線程產生的任何其他線程顯式設置,這將是“有用的”,因此取決於操作系統,通過調用pthread_create ,如果您沒有設置屬性 schedule/priority,則默認可能是從當前線程繼承(請參閱pthread_attr_init的文檔)。

同樣,應該強調的是,由於您使用的是 POSIX 線程調度,因此在使用那里的代碼時,您必須了解您所針對的操作系統(我相信您的老師沒有提到,並且可能不會) t)。

具體到您的代碼,您的線程根本無法運行的原因可能是由於您的sched_get_priority_min不接受0作為優先級(同樣,取決於您的操作系統); 如果你做了這樣的事情:

printf("min: %d\n", sched_get_priority_min(SCHED_RR));
int err = pthread_attr_setschedparam(&tattr2,&param2);
if (err != 0) {
    printf("pthread_attr_setschedparam error: %s\n", strerror(err));
}

您可能會看到 output 類似於以下內容:

min: 1
pthread_attr_setschedparam: Invalid argument

Invalid argument是因為您將優先級設置為0 ,但 SCHED_RR 的操作系統線程調度策略不接受0 (很多SCHED_RR內核不接受,但 BSD 內核可以,如果您在類似的東西上編譯相同的代碼macOS 或 OpenBSD 將“按預期”編譯和運行)。


同樣,這完全依賴於操作系統(甚至 kernel 版本)。 可以通過各種方式編譯Linux kernel來處理線程調度; 因此,雖然我知道這個問題是關於一個學術問題,但了解您使用這樣的代碼所針對的操作系統 kernel 非常重要。


順便說一句,我知道您不想要作業的答案,但我會告訴您,您的教授可能會讓您失敗,除非您使用的 RTOS 處理線程調度和優先級的方式與典型的“桌面”kernel。

我這么說的原因是因為您的代碼可能總是按照您啟動線程的順序運行,無論您設置什么調度/優先級(例如使用SCHED_FIFO而不是SCHED_RR ..參見調度策略)或您的 CPU 親和力。

線程優先級與線程啟動順序無關。 這不是優先級的工作方式。 優先級和調度是通過kernel的時間片調度策略,哪個線程應該首先獲得CPU資源。

要以 3/4-1/2 的順序啟動線程,您需要使用一個信號量或幾個互斥體以保證線程啟動順序。

例如:

.. headers ..

static pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mtx3 = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t mtx4 = PTHREAD_MUTEX_INITIALIZER;

void wait_to_start(pthread_mutex_t* mtx) {
    pthread_mutex_lock(mtx);
    pthread_mutex_unlock(mtx);
}

void* taskOne(void* p) { wait_to_start(&mtx1); printf("taskOne\n"); return 0; }
void* taskTwo(void* p) { wait_to_start(&mtx2); printf("taskTwo\n"); return 0; }
void* taskThree(void* p) { wait_to_start(&mtx3); printf("taskThree\n"); return 0; }
void* taskFour(void* p) { wait_to_start(&mtx4); printf("taskFour\n"); return 0; }

int main() {
    ... your other code here ..

    // grab all of the locks first in main thread
    pthread_mutex_lock(&mtx1);
    pthread_mutex_lock(&mtx2);
    pthread_mutex_lock(&mtx3);
    pthread_mutex_lock(&mtx4);

    // spin up threads
    pthread_t tid_1, tid_2, tid_3, tid_4;
    pthread_create(&tid_1, &tattr1, taskOne, NULL);
    pthread_create(&tid_2, &tattr1, taskTwo, NULL);
    pthread_create(&tid_3, &tattr2, taskThree, NULL);
    pthread_create(&tid_4, &tattr2, taskFour, NULL);

    // sequence point/time-slice (helps to ensure all threads actually start)
    usleep(1);

    pthread_mutex_unlock(&mtx3);
    pthread_mutex_unlock(&mtx4);
    
    // sequence point/time-slice (helps to ensure thread 3/4 actually continue)
    usleep(1);

    pthread_mutex_unlock(&mtx1);
    pthread_mutex_unlock(&mtx2);

    ... your final code here ...
}

我希望這可以幫助您更多地理解您的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM