簡體   English   中英

如何解決pthread_create錯誤(11)資源暫時不可用?

[英]How to solve pthread_create error (11) resource temporarily unavailable?

我正在使用 c 語言(使用 openwrt 作為操作系統)構建一個項目,以將文件上傳到 FTP 服務器。 我正在使用 MQTT 處理傳入數據。 因此,對於我訂閱的每個主題,我都會保存這些數據,然后將其上傳到 FTP 服務器並讓事情順利進行,每次我需要上傳文件時,我只需使用一個線程來完成這項工作。 並且為了確保程序不會運行太多線程,每個主題都可以創建一個線程。 我正在使用一個變量(比如互斥鎖,但它不是 pthread_mutex_t 因為我不需要阻塞線程,我想跳過該步驟並上傳下一個文件)。 我雖然使用這種技術我很安全,但是在運行程序 15 分鍾后,我收到此錯誤 11,它表示當程序嘗試創建線程 (pthread_create) 時資源暫時不可用。 我試圖找出可能是什么問題的嘗試之一是。

  • 我使用了 pthread_join() function 在我的情況下這不是一個選項,但只是為了確保每個線程都已完成並且不會在永久循環中運行。 該程序運行了一個多小時,並且錯誤沒有再次出現。 當然,每個線程都按預期完成。
  • 而且我 90% 確定每個主題都只在線程上創建,並且只有在前一個主題完成后才會創建下一個主題。 (我在線程創建之前和之后跟蹤變量的狀態)。
  • 我將最大線程從這里“/proc/sys/kernel/threads-max”設置為 2000(2000 綽綽有余,因為我沒有太多主題)

上傳 function (這將創建線程):

void uploadFile(<args...>, bool* locker_p){
    *locker_p = true;
    args->uploadLocker_p = uploadLocker_p;

    <do something here>

    pthread_t tid;
    int error = pthread_create(&tid, NULL, uploadFileThread, (void*)args);
        if(0 != error){
            printf("Couldn't run thread,(%d) => %s\n", error, strerror(error));
    }
        else{
            printf("Thread %d\n", tid);
    }
}

上傳線程:

void *uploadFileThread(void *arg){ 
    typeArgs* args = (typeArgs*)arg;

   <do something like upload the file>

    *(args->uploadLocker_p) = false;
    free(args);

    return NULL;
    //pthread_exit(0);
}

已創建線程的默認堆棧大小正在消耗過多的虛擬 memory。

從本質上講,kernel 是在告訴您的進程,它有太多虛擬 memory 已經在使用,它不敢再提供它了,因為如果進程突然使用它,沒有足夠的 RAM 和交換來備份它全部。

要解決此問題,請創建一個將每個線程堆棧限制為合理的屬性。 如果您的線程不使用 arrays 作為局部變量,或者進行深度遞歸,那么2*PTHREAD_STACK_MIN (來自<limits.h> )是一個很好的大小。 pthread_create()調用不使用該屬性,它只是一個配置塊,您可以將同一個用於您創建的任意數量的線程,或者為每個線程創建一個新的。

例子:

pthread_attr_t  attrs;
pthread_t       tid;
int             err;

pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2 * PTHREAD_STACK_MIN);
err = pthread_create(&tid, &attrs, uploadFileThread, (void *)args);
pthread_attr_destroy(&attrs);
if (err) {
    /* Failed, errno in err; use strerror(err) */
} else {
    /* Succeeded */
}

還要記住,如果你的uploadFileThread()分配了memory,線程退出時它不會被自動釋放。 看起來 OP 已經知道這一點(因為他們有 function 在准備退出時釋放參數結構),但我認為指出它是個好主意。


就個人而言,我更喜歡使用線程池。 這個想法是上傳工作人員是預先創建的,他們將等待新的工作。 這是一個例子:

pthread_mutex_t        workers_lock;
pthread_mutex_t        workers_wait;
volatile struct work  *workers_work;
volatile int           workers_idle;
volatile sig_atomic_t  workers_exit = 0;

其中struct work是一個由workers_lock保護的單鏈表, workers_idle初始化為零並在等待新工作時遞增, workers_wait是一個條件變量,當新工作到達workers_lock時發出信號, workers_exit是一個計數器,當非零時,告訴許多工人退出。

一個工人基本上是一些東西

void worker_do(struct work *job)
{
   /* Whatever handling a struct job needs ... */
}

void *worker_function(void *payload __attribute__((unused)))
{
    /* Grab the lock. */
    pthread_mutex_lock(&workers_lock);

    /* Job loop. */
    while (!workers_exit) {
        if (workers_work) {
            /* Detach first work in chain. */
            struct work *job = workers_work;
            workers_work = job->next;
            job->next = NULL;

            /* Work is done without holding the mutex. */
            pthread_mutex_unlock(&workers_lock);
            worker_do(job);
            pthread_mutex_lock(&workers_lock);
            continue;
        }

        /* We're idle, holding the lock. Wait for new work. */
        ++workers_idle;
        pthread_cond_wait(&workers_wait, &workers_lock);
        --workers_idle;
    }

    /* This worker exits. */
    --workers_exit;

    pthread_mutex_unlock(&workers_lock);
    return NULL;
}

連接處理過程可以使用idle_workers()來檢查空閑工作人員的數量,並且要么增加工作線程池,要么拒絕連接因為太忙。 idle_workers()類似於

static inline int  idle_workers(void)
{
    int  result;
    pthread_mutex_lock(&workers_lock);
    result = workers_idle;
    pthread_mutex_unlock(&workers_lock);
    return result;
}

請注意,每個工作人員僅在很短的時間內持有鎖,因此idle_workers()調用不會長時間阻塞。 pthread_cond_wait()在開始等待信號時自動釋放鎖,只有在重新獲得鎖后才返回。)

accept()中等待新連接時,將套接字設置為非阻塞並使用poll()等待新連接。 如果超時通過,檢查工人的數量,並在必要時通過調用reduce_workers(1)或類似方法來減少它們:

void reduce_workers(int number)
{
    pthread_mutex_lock(&workers_lock);
    if (workers_exit < number) {
        workers_exit = number;
        pthread_cond_broadcast(&workers_wait);
    }
    pthread_mutex_unlock(&workers_lock);
}

為了避免必須為每個線程調用pthread_join() ——我們甚至不知道這里退出了哪些線程——為了獲取/釋放與線程相關的 kernel 和 C 庫元數據,需要分離工作線程。 創建工作線程tid成功后,調用pthread_detach(tid); .

當一個新的連接到達並且確定應該委托給工作線程時,您可以但不必檢查空閑線程的數量,創建新的工作線程,拒絕上傳,或者只是 append工作到隊列中,以便“最終”得到處理。

暫無
暫無

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

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