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