2011-02-10 9 views
7

Tôi đã thực hiện một chương trình C khá đơn giản để tính toán các điều khoản của chuỗi Fibonacci, mà tôi đang chạy trên Ubuntu. Tôi đã tạo ra một số cấu trúc dữ liệu khá vụng về để nó có thể làm các số nguyên rất lớn, nhưng các chi tiết cụ thể của chương trình không quan trọng lắm - điều quan trọng là chương trình có thể mất nhiều thời gian để thực hiện các tính toán.Chạy chương trình C với nhiều luồng trong nền khi nó yêu cầu đầu vào của người dùng

Vì tò mò, tôi quyết định làm cho chương trình bắt đầu tính toán và sau đó cho phép người dùng nhập một ký tự để xem cách tính toán bao xa. Vì vậy, trong trường hợp này, nếu chương trình đang tính toán thuật ngữ thứ n của dãy Fibonacci và nó chưa được thực hiện, thì nhập số '1' sẽ nhận được chương trình xuất kết quả k mà nó hiện đang tính toán. Tôi đã cố gắng làm điều đó bằng cách sử dụng cách tiếp cận sau.

Chương trình sử dụng scanf để nhận số nguyên dài đại diện cho thuật ngữ của chuỗi cần được tính toán. Chương trình sau đó tạo một luồng với thường trình mà tôi đã viết để tính toán và in thuật ngữ Fibonacci n'th, thoát ra khi nó đã hoàn thành việc đó. Sau đó, chương trình tạo ra một biến số nguyên i được sử dụng để lưu trữ đầu vào và khởi tạo nó để một số giá trị khác 0. Sau đó nó liên tục đi vào một vòng lặp while, miễn là int đó khác 0, và trong mỗi lần lặp của vòng lặp nó thực hiện một scanf ("% d", & i). Sau đó nó so sánh giá trị đó với 1 và nếu đó là 1 thì nó sẽ in giá trị của bộ đếm mà tôi đã thiết lập để theo dõi tiến trình tính toán Fibonacci.

Dù sao, tất cả các công việc trên đều rất trơn tru mặc dù tôi đang quá sâu sắc với những thứ như chủ đề. Tuy nhiên, vấn đề mà tôi đang gặp phải là khi tôi phải tính toán, thuật ngữ thứ triệu của chuỗi chương trình mất vài phút để hoàn thành, và sẽ thật tuyệt nếu chỉ chạy nó trong nền. Tuy nhiên, nếu tôi đặt quá trình trong nền với ctrl + z và sau đó gõ bg, quá trình bắt đầu nhưng ngay lập tức dừng lại. Tôi đoán rằng điều này là bởi vì nó liên tục đòi hỏi đầu vào từ người dùng và do đó nó dừng lại cho đến khi nó nhận được đầu vào đó.

Bất kỳ đề xuất nào về cách phá vỡ vấn đề trên sẽ được đánh giá cao. Tôi không đặc biệt lo lắng về vấn đề cụ thể này (tính toán các số Fibonacci) vì đó chỉ là một vấn đề khá ngẫu nhiên mà tôi đã chọn để sử dụng cho việc tính toán. Tôi quan tâm hơn đến vấn đề chung của việc tạo ra một cách cơ bản để người dùng nhập các lệnh vào chương trình, chương trình sẽ thực hiện trong các luồng riêng biệt, nhưng vẫn cho phép người dùng chạy chương trình trong nền nếu cần.

Xin lỗi cho câu hỏi khá dài và cảm ơn trước sự giúp đỡ của bạn!

Phil

Chỉnh sửa: Theo yêu cầu, tôi đã thêm (một phiên bản rất đơn giản) mã ở đây. Ý tưởng cơ bản là giống nhau: chương trình khởi chạy tính toán dài trong một chuỗi mới, sau đó lặp lại các đầu vào bằng scanf. Nhập 0 quits chương trình, nhập 1 sẽ hiển thị một bộ đếm cho biết tiến trình tính toán. Tôi muốn để có thể chạy chương trình trong nền, nhưng vì nó liên tục yêu cầu đầu vào nó dừng quá trình ngay lập tức. Bỏ qua tràn số học trên bộ đếm; chương trình thực tế của tôi có cấu trúc dữ liệu để xử lý loại nội dung này nhưng tôi đã cố gắng đơn giản hóa mã của mình càng nhiều càng tốt để có thể đọc được.

//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

Thay vì mô tả dài dòng, bạn có thể xem xét đăng mã của mình :-) –

+0

Điểm được chụp! Hy vọng rằng điều này làm rõ mọi thứ lên một chút. – ptmx

+2

Tôi khuyên bạn nên sử dụng trình xử lý tín hiệu thay vì nhập bằng 'scanf' ... Sau đó,' kill -SIGUSR1 <ứng dụng của bạn pid> 'có thể được sử dụng để kích hoạt báo cáo của bạn ngay cả khi ứng dụng được tạo nền. – JimR

Trả lời

1

Giả sử rằng bạn đang sử dụng POSIX Threads, bạn có thể scanf trong một thread, để cho nó chặn cho đến khi một cái gì đó được nhập vào, và sau đó pthread_cond_signal thread khác để làm bất cứ điều gì bạn muốn nó làm. Bạn cũng có thể khai báo một biến được cập nhật bởi chuỗi tính toán và đọc theo luồng với scanf trong đó.Một cách tinh vi hơn là lắng nghe trên một ổ cắm cho các tin nhắn gửi đến, và có một phần thông dịch viên đọc từ ổ cắm đó và ghi lại các kết quả. Trong trường hợp đó, bạn không cần scanf và chương trình của bạn có thể chạy ở chế độ nền.

+0

Điều này sẽ không giúp được vấn đề về nền tảng của OP và quá trình dừng lại trên thiết bị đầu cuối được đọc. –

+0

Báo hiệu luồng khác không phải là vấn đề; rằng một phần hoạt động tốt vì vậy tôi không nghĩ rằng việc sử dụng tín hiệu hoặc sử dụng một biến như mô tả ở trên sẽ giải quyết vấn đề này. Tôi không quen với ổ cắm, nhưng tôi sẽ tìm hiểu về những cái đó và xem liệu cách tiếp cận đó có giúp ích gì không. – ptmx

+0

Vâng, nếu nó dừng lại ở 'scanf' khi bạn đặt chương trình ở chế độ nền, đó là vì quá trình nền không có' stdin', 'stdouy' và' stderr' hợp lệ.Vì vậy, kể từ khi cả hai chủ đề là về mặt kỹ thuật dưới cùng một không gian quá trình, và lập kế hoạch của các chủ đề được thực hiện với bộ lập lịch pthread, khi toàn bộ quá trình dừng lại, cả hai chủ đề dừng lại. Bạn cần phải đóng tất cả các mô tả tập tin chuẩn trước khi đặt chương trình của bạn ở chế độ nền. – Peyman

1

tôi sẽ đề nghị một cách tiếp cận khác nhau:

Tôi có một chương trình xử lý gigabyte dữ liệu và tôi muốn xem tôi đang ở đâu trong tiến trình. Điều này đòi hỏi một khối dữ liệu lớn được in, vì vậy tôi chỉ muốn làm điều đó khi được hỏi. Tôi đã thêm một trình xử lý tín hiệu vào SIGTSTP (phím điều khiển-z sẽ gửi tín hiệu này) sẽ in ra dữ liệu. Điều này không yêu cầu một luồng riêng biệt, dễ thực hiện và cũng cho phép bạn báo hiệu nó nếu bạn bắt đầu quá trình ở nơi khác (vì bạn chỉ có thể gửi tín hiệu qua kill)

1

Vấn đề cơ bản là ngay khi bạn cố gắng đọc từ stdin khi bạn đang ở chế độ nền, bạn sẽ nhận được SIGSTOP, có nghĩa là nó như thể bạn đã nhấn ctrl-z một lần nữa ngay lập tức. Nếu bạn cần chạy ở chế độ nền, thay đổi tương đối simlpe ở đây là đọc từ một năm mươi thay vì đọc từ stdin. Sử dụng lệnh mkfifo để tạo ra một cái giếng knownfifo như vậy:

mkfifo fib.fifo 

Sự thay đổi vòng lặp chính của bạn như sau:


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 
    } 


Các lần đọc sẽ chặn cho đến khi một cái gì đó được viết. Bạn phải cẩn thận ở đây nếu bạn có kế hoạch để chạy nhiều trường hợp này cùng một lúc. Một khi một mục được đọc từ một năm mươi, nó đã biến mất. Chỉ một trường hợp sẽ thấy giá trị bạn đã viết. Bạn có thể ghi vào tệp đơn giản là "echo 1 >> fib.fifo"

1

Bạn có thể ngăn chương trình dừng khi nó cố đọc từ thiết bị đầu cuối trong khi được chặn bằng cách chặn hoặc xử lý SIGTTIN (và tương tự SIGTTOU để viết đến nhà ga).

Trong khi chương trình của bạn ở chế độ nền, SIGTTINSIGTTOU được phân phối khi nó đọc hoặc ghi vào thiết bị đầu cuối tương ứng. Theo mặc định, chương trình của bạn bị dừng khi nhận được các tín hiệu này.

Các bit sau của khối mã SIGTTINSIGTTOU sử dụng pthread_sigmask() (kể từ khi chương trình của bạn sử dụng chủ đề - nếu không sigprocmask() có thể được sử dụng):

#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 */ 

ở trên nên được thực hiện trước khi tạo chủ đề của bạn, vì mới chủ đề kế thừa mặt nạ tín hiệu. Điều này sẽ cho phép chương trình của bạn tiếp tục chạy trong khi viết và (cố gắng) đọc từ bảng điều khiển.