我希望有人能幫助我。在過去的十年中,我沒有用C語言編寫太多的代碼,並且在兩天之前就選擇了這個代碼,所以請耐心等待,因爲我很生疏。謝謝!C代碼堆棧損壞改變變量
什麼:
我正在創建一個應用程序非常簡單的線程池。這段代碼是用編譯器的GNU GCC編寫在C代碼塊上的。它是作爲命令行應用程序構建的。沒有附加文件被鏈接或包含。
代碼應該創建X個線程(在這種情況下,我將它設置爲10),每個線程在觀察數組條目(由線程線程索引或計數所標識的數組條目)時都會坐着並等待它可能需要的任何傳入數據處理。一旦給定的孩子處理了通過數組進入的數據,就不需要將數據傳遞迴主線程;相反,孩子應該簡單地將該數組條目重置爲0以表示它已準備好處理另一個輸入。主線程將接收請求,並將它們分發給任何可用的線程。如果沒有可用的,那麼它將拒絕處理該輸入。
爲了簡單起見,下面的代碼是確實表現出我試圖追查堆棧溢出一個完整的工作,但修剪和去內臟的版本。編譯沒有問題,最初運行正常但也有少數通過在子線程過程中threadIndex值之後(的WorkerThread)被損壞並跳轉到怪異的價值觀 - 通常成爲我已經把在「睡眠」功能的毫秒數。
我已經檢查:
- 的threadIndex變量不是全局或共享變量。
- 所有的數組都是足夠大到足以處理線程,我創建的最大數量。
- 所有迴路有loopvariable重置爲0之前運行。
- 我還沒有命名具有相同名稱的多個變量。
- 我用atomic_load,以確保我不會在一次寫入同一個全局數組變量有兩個不同的線程請注意我生疏......我可誤解這部分是如何工作的
- 我已經把測試用例遍歷,看看變量發生什麼變化,我很難過。
最佳推測
所有我的研究證實了我從幾年前召回;我可能會在某個地方出界並導致堆棧損壞。我已經在google和堆棧溢出中看到了許多其他類似的問題,同時我也指出了相同的結論,我一直無法弄清楚我的代碼中有什麼特別的錯誤。
#include<stdio.h>
//#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<conio.h>
//#include<unistd.h>
#define ESCAPE 27
int maxThreads = 10;
pthread_t tid[21];
int ret[21];
int threadIncoming[21];
int threadRunning[21];
struct arg_struct {
char* arg1;
int arg2;
};
//sick of the stupid upper/lowercase nonsense... boom... fixed
void* sleep(int time){Sleep(time);}
void* workerThread(void *arguments)
{
//get the stuff passed in to us
struct arg_struct *args = (struct arg_struct *)arguments;
char *address = args -> arg1;
int threadIndex = args -> arg2;
//hold how many we have processed - we are unlikely to ever hit the max so no need to round robin this number at this point
unsigned long processedCount = 0;
//this never triggers so it IS coming in correctly
if(threadIndex > 20){
printf("INIT ERROR! ThreadIndex = %d", threadIndex);
sleep(1000);
}
unsigned long x = 0;
pthread_t id = pthread_self();
//as long as we should be running
while(__atomic_load_n (&threadRunning[threadIndex], __ATOMIC_ACQUIRE)){
//if and only if we have something to do...
if(__atomic_load_n (&threadIncoming[threadIndex], __ATOMIC_ACQUIRE)){
//simulate us doing something
//for(x=0; x<(0xFFFFFFF);x++);
sleep(2001);
//the value going into sleep is CLEARLY somehow ending up in index because you can change that to any number you want
//and next thing you know the next line says "First thread processing done on (the value given to sleep)
printf("\n First thread processing done on %d\n", threadIndex);
//all done doing something so clear the incoming so we can reuse it for our next one
//this error should not EVER be able to get thrown but it is.... something is corrupting our stack and going into memory that it shouldn't
if(threadIndex > 20){ printf("ERROR! ThreadIndex = %d", threadIndex); }
else{ __atomic_store_n (&threadIncoming[threadIndex], 0, __ATOMIC_RELEASE); }
//increment the processed count
++processedCount;
}
else{Sleep(10);}
}
//no need to do atomocity I don't think for this as it is only set on the exit and not read till after everything is done
ret[threadIndex] = processedCount;
pthread_exit(&ret[threadIndex]);
return NULL;
}
int main(void)
{
int i = 0;
int err;
int *ptr[21];
int doLoop = 1;
//initialize these all to set the threads to running and the status on incoming to NOT be processing
for(i=0;i < maxThreads;i++){
threadIncoming[i] = 0;
threadRunning[i] = 1;
}
//create our threads
for(i=0;i < maxThreads;i++)
{
struct arg_struct args;
args.arg1 = "here";
args.arg2 = i;
err = pthread_create(&(tid[i]), NULL, &workerThread, (void *)&args);
if (err != 0){ printf("\ncan't create thread :[%s]", strerror(err)); }
}
//loop until we hit escape
while(doLoop){
//see if we were pressed escape
if(kbhit()){ if(getch() == ESCAPE){ doLoop = 0; } }
//just for testing - actual version would load only as needed
for(i=0;i < maxThreads;i++){
//make sure we synchronize so we don't end up pointing into a garbage address or half loading when a thread accesses us or whatever was going on
if(!__atomic_load_n (&threadIncoming[i], __ATOMIC_ACQUIRE)){
__atomic_store_n (&threadIncoming[i], 1, __ATOMIC_RELEASE);
}
}
}
//exiting...
printf("\n'Esc' pressed. Now exiting...\n");
//call to end them all...
for(i=0;i < maxThreads;i++){ __atomic_store_n (&threadRunning[i], 0, __ATOMIC_RELEASE); }
//join them all back up - if we had an actual worthwhile value here we could use it
for(i=0;i < maxThreads;i++){
pthread_join(tid[i], (void**)&(ptr[i]));
printf("\n return value from thread %d is [%d]\n", i, *ptr[i]);
}
return 0;
}
輸出
這裏是輸出我得到。請注意,在開始瘋狂之前需要多長時間,似乎可能會有所不同,但不會太多。
'一個非常簡單的線程池' - 看起來並不那麼簡單。通常,「不簡單」意味着'不能正常工作',尤其是當應用於mutithreaded應用程序時。大多數實際上簡單的線程池將任務排列到工作線程,這些線程圍繞阻塞的生產者 - 消費者隊列進行循環。這是衆所周知的設計,運行良好,不需要浪費CPU輪詢,延遲添加Sleep()循環或任何類型的「管理線程」。在數組中微管理線程實例是複雜的,浪費的,幾乎肯定會出錯。果然...... :) –
'else {Sleep(10);}'這似乎有問題,它爲什麼在那裏? [在多線程應用程序中使用Sleep(n)](http://www.flounder.com/badprogram.htm#Sleep)。 – Lundin
^^是的,大部分嘗試避免/減輕一些其他問題,可能投票CPU使用:(無論如何,它肯定是誤用睡眠()。 –