簡體   English   中英

公平的關鍵部分(Linux)

[英]Fair critical section (Linux)

在多線程Linux應用程序中,我使用互斥鎖來處理關鍵部分。 除公平問題外,這種方法效果很好。 一個線程離開臨界區並立即重新進入並不會給任何其他線程帶來機會。 例如

while(true)
{
    critsect.enter();
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
}

可能很可能會阻止任何其他線程進入同一個關鍵部分。 互斥是不公平的。

是否有解決方案來制定公平的關鍵部分? 我正在考慮添加一個隊列,以便按照“到達”的順序執行關鍵部分。 或者至少一個計數器可能在解鎖后執行pthread_yield(),如果其他線程正在等待。

是否有針對此類要求的推薦做法?

您可以在這些行上在pthreads互斥鎖之上構建FIFO“票證鎖定”:

#include <pthread.h>

typedef struct ticket_lock {
    pthread_cond_t cond;
    pthread_mutex_t mutex;
    unsigned long queue_head, queue_tail;
} ticket_lock_t;

#define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }

void ticket_lock(ticket_lock_t *ticket)
{
    unsigned long queue_me;

    pthread_mutex_lock(&ticket->mutex);
    queue_me = ticket->queue_tail++;
    while (queue_me != ticket->queue_head)
    {
        pthread_cond_wait(&ticket->cond, &ticket->mutex);
    }
    pthread_mutex_unlock(&ticket->mutex);
}

void ticket_unlock(ticket_lock_t *ticket)
{
    pthread_mutex_lock(&ticket->mutex);
    ticket->queue_head++;
    pthread_cond_broadcast(&ticket->cond);
    pthread_mutex_unlock(&ticket->mutex);
}

在這種方案下,當線程在受到故障鎖定保護的關鍵部分內時,不保留低級pthreads互斥鎖,允許其他線程加入隊列。

即使有一個公平的關鍵部分,代碼可能會有可怕的性能,因為如果關鍵部分被持有很長一段時間,線程將經常等待它。

所以我建議你嘗試重構代碼,這樣就不需要在很長一段時間內鎖定關鍵部分。 通過完全使用不同的方法(通常建議通過消息隊列傳遞對象,因為它很容易正確)或者至少通過對局部變量進行大部分計算而不保持鎖定而不是僅使用鎖來存儲結果。 如果鎖定保持較短的時間段,則線程將花費較少的時間等待它,這通常會提高性能並使公平性成為無問題。 您還可以嘗試增加鎖粒度(單獨鎖定較小的對象),這也將減少爭用。

編輯:好的,考慮一下,我相信Linux中的每個關鍵部分都是公平的。 每當有睡眠者時,解鎖操作必須進入內核以告訴它喚醒它們。 在從內核返回期間,調度程序運行並選擇具有最高優先級的進程。 在等待時,睡眠者會優先上升,因此在某些時候他們會高到足以讓釋放任務變得更快。

恕我直言,您可以在Linux上使用FIFO SCHEDULER並更改線程的優先級:

thread_func() {
    ... 
    pthread_t t_id = pthread_self();
    struct sched_param prio_zero, prio_one;
    prio_zero.sched_priority = sched_get_priority_min(SCHED_FIFO);
    prio_one.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
    phtread_setschedparam(t_id, SCHED_FIFO, &prio_zero);
    ...
    while(true)
    {
        ... Doing something before
        phtread_setschedparam(t_id, SCHED_FIFO, &prio_one);
        critsect.enter();
        ... do calculations ...
        ... maybe call a blocking operation so we sleep ...
        critsect.leave();
        phtread_setschedparam(t_id, SCHED_FIFO, &prio_zero);
        ... Do something after
    }
}

如果您的聲明成立(我沒有時間閱讀,看起來好像您在發布問題之前已經研究了這個),我建議

 sleep(0);

在關鍵部分之間明確屈服。

while(true)
{
    critsect.enter();
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
    sleep(0);
}

好的,這個怎么樣:

while(true)
{
    sema.wait;
    critsect.enter();
    sema.post;
    ... do calculations ...
    ... maybe call a blocking operation so we sleep ...
    critsect.leave();
}

在里面。 信號量計數為1.在嘗試獲取CS並在完成時發出信號之前,還有其他線程在信號量上等待。 如果'calculate'線程獲得sema,它可以到達CS並鎖定它。 一旦進入鎖定狀態,但在長時間計算之前,sema會發出信號,然后另一個線程可以到達CS但不會進入其中。 當'calculate'線程退出鎖時,它不能循環並重新鎖定它,因為sema。 count為零,因此另一個線程獲得鎖定。 'calculate'線程必須等待sema,直到進入的另一個線程完成其訪問並發出sema信號。

通過這種方式,另一個線程可以“保留”對數據的訪問,即使它實際上還沒有實現。

Rgds,馬丁

暫無
暫無

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

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