2011-02-10 27 views
7

我做了一個相當簡單的C程序來計算我在Ubuntu上運行的Fibonacci序列的條款。我做了一些相當笨拙的數據結構,以便它可以做很大的整數,但程序的細節並不重要 - 重要的是程序可能需要相當長的時間來執行計算。在需要用戶輸入時在後臺運行帶有多個線程的C程序

出於好奇,我決定讓程序開始計算,然後讓用戶輸入一個字符來查看計算結果有多遠。所以在這種情況下,如果程序正在計算斐波那契數列的第n項並且還沒有完成,輸入數字'1'將使程序輸出當前正在計算的項k。我試圖用下面的方法做到這一點。

該程序使用scanf獲得一個長整數,它表示需要計算的序列的期限。程序然後創建一個線程,其中包含我編寫的用於計算和打印第n個斐波那契項的例程,該術語一旦完成即可退出。接下來,程序創建一個整型變量,用於存儲輸入並將其初始化爲非零值。然後,只要該int不爲零,它就會不斷進入while循環,並在循環的每次迭代中執行scanf(「%d」,& i)。然後它將該值與1進行比較,如果它是1,那麼它將打印我設置的計數器的值,以跟蹤斐波那契計算的進度。

無論如何,以上所有工作都非常順利,儘管我的線程深度問題非常嚴重。然而,我遇到的問題是,當我必須計算序列的第100萬個字節時,程序需要花費幾分鐘才能完成,並且只需在後臺運行它就會很好。但是,如果我用ctrl + z在後臺放置進程,然後鍵入bg,則進程啓動但立即再次停止。我猜測這是因爲它經常需要用戶輸入,因此它會停下來,直到它獲得輸入。

任何有關如何規避上述問題的建議將不勝感激。我並不特別擔心這個特定的問題(計算斐波納契數),因爲這只是我選擇用於計算的一個相當隨機的問題。我更感興趣的是創建用戶向程序輸入命令的基本方式的一般問題,程序然後在不同的線程中執行程序,但仍然允許用戶在必要時在後臺運行程序。

道歉的相當冗長的問題,並提前感謝任何幫助!

菲爾

編輯:根據要求,我加了(非常簡化的版本),這裏的代碼。基本思想是一樣的:程序在新線程中啓動一個冗長的計算,然後用scanf循環輸入。輸入0退出程序,輸入1顯示一個指示計算進度的計數器。我希望能夠在後臺運行該程序,但由於它不斷要求輸入,因此立即停止該過程。忽略計數器上的算術溢出;我的實際程序有處理這種東西的數據結構,但我試圖儘可能簡化我的代碼以提高可讀性。

//Simple program to test threading and input. 
#include <stdio.h> 
#include <pthread.h> 
#define NUM_THREADS 2 

void *stuff(); 
int counter; //keeps track of the progress of the computation 

int main(){ 
    counter=1; 
    pthread_t threads[NUM_THREADS]; 
    pthread_create(&threads[0], NULL, stuff, NULL); 

    //loop while the input is non-zero so that the program can 
    //accept commands 
    int input=10; 
    while(input){ 
    input=10; 
    printf("Enter 0 to exit or 1 to display progress: "); 
    scanf("%d", &input); 
    if(input==1){ 
     printf("Currently iterating for the %dth time.\n", counter); 
    } 
    } 

return 0; 
} 

//Randomly chosen computation that takes a while. 
void *stuff(){ 
    long i,j,n=1000000000; 
    for(i=0; i<=n; i++){ 
    for(j=0; j<=n; j++){ 
     i*i*i*i*i*i*i*i*i*i*i; 
     j*j*j*j*j*j*j*j*j*j*j; 
     counter++; 
    } 
    } 
    printf("Done.\n"); 
    pthread_exit(NULL); 
} 
+0

取而代之的是冗長的描述,你可以考慮發佈你的代碼:-) – 2011-02-10 04:26:42

+0

採取的點!希望這可以讓事情稍微有點清晰。 – ptmx 2011-02-10 06:16:16

回答

1

假設你正在使用POSIX線程,你可以scanf在一個線程,讓它阻止進入到東西,然後pthread_cond_signal其他線程做任何你想做的事情。您還可以聲明一個變量,該變量由計算線程更新並由其中的scanf線程讀取。另一種更復雜的方法是在傳入消息的套接字上偵聽,並且有一個消息解釋器部分從該套接字讀取並寫回結果。在這種情況下,你不需要scanf,你的程序可以在後臺運行。

1

我會建議不同的方法:

我有處理千兆字節的數據的程序,我想看看我的過程,其中。這需要打印大量的數據,所以我只想在詢問時做。我添加了一個信號處理程序到SIGTSTP(control-z keystroke發送此信號),它將打印出數據。這不需要單獨的線程,實施起來很簡單,並且如果您在其他地方啓動該流程(因爲您可以通過kill發送信號),您也可以發信號通知

1

基本問題是隻要您嘗試從標準輸入讀取,當你在後臺,你會得到SIGSTOP,這意味着它就好像你再次點擊ctrl-z。如果你需要在後臺運行,這裏的一個相對比較簡單的變化是從fifo讀取,而不是從stdin讀取。使用mkfifo命令來創建一個良好knownfifo像這樣:

mkfifo fib.fifo 

改變你的主循環,像這樣:


int main(){ 
    int intput = 10; 
    FILE * handle = fopen("fib.fifo", "r"); 
    if(!handle){ 
    //do something error } 
    while(1){ 
    //you might want to use fgets instead of fscanf 
    fscanf(handle, "%d", &input); 
    //now do whatever with input 
    } 


的讀取將阻塞,直到一些被寫入。如果您計劃同時運行多個實例,則必須謹慎。一旦一個項目從FIFO中讀取,它就消失了。只有一個實例會看到你寫的值。您可以簡單地寫入文件,如「echo 1 >> fib.fifo」

1

您可以防止程序在嘗試從終端讀取時停止,同時通過阻止或處理SIGTTIN(以及類似地SIGTTOU用於寫入到終端)。

當您的程序在後臺時,分別從終端讀取或向終端寫入時,會發送SIGTTINSIGTTOU。默認情況下,程序在收到這些信號時會停止。

使用pthread_sigmask()代碼塊SIGTTINSIGTTOU的下位(因爲你的程序使用線程 - 否則sigprocmask()可以使用):

#include <signal.h> 

/* ... */ 

sigset_t sset; 
sigemptyset(&sset); 
sigaddset(&sset,SIGTTIN); 
sigaddset(&sset,SIGTTOU); 
pthread_sigmask(SIGBLOCK,&sset,NULL); /* should check return for errors.. 
              returns 0 on success */ 

以上應您之前創建的線程完成,因爲新線程繼承信號掩碼。這將允許您的程序在寫入和(試圖)從控制檯讀取時繼續運行。

相關問題