当需要用户输入时在后台运行具有多个线程的 C 程序
我编写了一个相当简单的 C 程序来计算斐波那契数列的项,我在 Ubuntu 上运行该程序。我制作了一些相当笨拙的数据结构,以便它可以处理非常大的整数,但程序的细节并不是非常重要——重要的是程序可能需要相当长的时间来执行计算。
出于好奇,我决定让程序开始计算,然后允许用户输入一个字符来查看计算进行了多远。因此,在这种情况下,如果程序正在计算斐波那契数列的第 n 项并且尚未完成,则输入数字“1”将使程序输出当前正在计算的项 k。我尝试使用以下方法来做到这一点。
该程序使用 scanf 来获取一个长整数,该整数表示需要计算的序列的项。然后,该程序使用我编写的例程创建一个线程,用于计算和打印第 n 个斐波那契项,该线程完成后退出。接下来,程序创建一个整数变量,用于存储输入并将其初始化为一些非零值。然后,只要该 int 非零,它就会连续进入 while 循环,并且在循环的每次迭代中,它都会执行 scanf("%d", &i)。然后它将该值与 1 进行比较,如果是 1,那么它将打印我设置的用于跟踪斐波那契计算进度的计数器的值。
不管怎样,尽管我对线程之类的东西非常不了解,但上述所有工作都非常顺利。然而,我遇到的问题是,当我必须计算序列的第一百万项时,程序需要几分钟才能完成,并且最好在后台运行它。但是,如果我使用 ctrl+z 将进程置于后台,然后输入 bg,进程将启动但立即再次停止。我猜测这是因为它不断需要用户的输入,因此它会停止直到获得该输入。
任何有关如何规避上述问题的建议将不胜感激。我并不特别担心这个特定问题(斐波那契数的计算),因为这只是我选择用于计算的一个相当随机的问题。我更感兴趣的是为用户创建一种向程序输入命令的基本方式的一般问题,然后程序在单独的线程中执行该命令,但仍然允许用户在必要时在后台运行程序。
对于这个相当冗长的问题表示歉意,并提前感谢您的帮助!
Phil
编辑:根据要求,我在此处添加了代码(非常简化的版本)。基本思想是相同的:程序在新线程中启动冗长的计算,然后使用 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);
}
I've made a fairly simple C program to compute the terms of Fibonacci sequence, which I'm running on Ubuntu. I made some rather clumsy data structures so that it can do very large integers, but the specifics of the program aren't terribly important -- what matters is that the program can take quite a while to perform the computations.
Out of curiosity, I decided I'd like to make the program start the computation and then allow the user to input a character to see how far along the computation is. So in this case, if the program is computing the n'th term of the Fibonacci sequence and it isn't done yet, inputting the number '1' will get the program to output the term k that it is currently computing. I've attempted to do that using the following approach.
The program uses scanf to get a long integer which represents the term of the sequence that needs to be computed. The program then creates a thread with the routine that I wrote to compute and print the n'th Fibonacci term, which exits once it has finished doing that. Following that, the program creates an integer variable i used to store input and initialises it so some non-zero value. It then continuously enters a while loop so long as that int is non-zero, and in each iteration of the loop it performs a scanf("%d", &i). It then compares that value to 1, and if it's 1 then it'll print the value of a counter that I've set up to track the progress of the Fibonacci computation.
Anyway, all of the above works very smoothly despite my being terribly out of depth with things like threads. However, the problem that I'm having is that when I have to compute, say, the millionth term of the sequence the program takes several minutes to finish, and it would be nice to simply run it in the background. However, if I put the process in the background with ctrl+z and then type bg, the process starts but immediately stops again. I'm guessing that this is because it is constantly requiring input from the user and thus it stops until it gets that input.
Any suggestions for how to circumvent the above issue would be greatly appreciated. I'm not especially worried about this specific issue (computation of the Fibonacci numbers) since that's just a fairly random problem I chose to use for the computation. I'm more interested in the general problem of creating a basic way for the user to input commands to the program, which the program then executes in separate threads, but which still enables the user to run the program in the background if necessary.
Apologies for the rather long-winded question, and thanks in advance for any help!
Phil
Edit: By request, I added (a very simplified version of) the code here. The basic idea is the same: the program launches a lengthy computation in a new thread, then loops for input with scanf. Inputting 0 quits the program, inputting 1 displays a counter indicating the progress of the computation. I'd like to be able to run the program in the background, but since it's continuously asking for input it stops the process immediately. Ignore the arithmetic overflow on the counter; my actual program has data structures to deal with this kind of stuff but I tried to simplify my code as much as possible for readability.
//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);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
假设您使用的是 POSIX 线程,您可以在一个线程中
scanf
让它阻塞,直到输入某些内容,然后pthread_cond_signal
另一个线程执行您想要的任何操作去做。您还可以声明一个变量,该变量由计算线程更新并由其中包含scanf
的线程读取。另一种更复杂的方法是在套接字上侦听传入消息,并使用消息解释器部分从该套接字读取数据并写回结果。在这种情况下,您不需要scanf
,并且您的程序可以在后台运行。Assuming that you're using POSIX Threads, you can
scanf
in one thread, let it block until something is entered, and thenpthread_cond_signal
the other thread to do whatever you want it to do. You can also declare a variable which is updated by the calculating thread and read by the thread withscanf
in it. Another more sophisticated way is to listen on a socket for incoming messages, and have a message interpreter part which reads from that socket, and writes back the results. In that case you don't need thescanf
, and your program can be running in background.我建议采用不同的方法:
我有一个处理千兆字节数据的程序,我想看看我在这个过程中处于什么位置。这需要打印一大块数据,所以我只想在被要求时才这样做。我向
SIGTSTP
添加了一个信号处理程序(control-z 按键发送此信号),该处理程序打印出数据。这不需要单独的线程,实现起来很简单,并且还允许您在其他地方启动进程时向它发出信号(因为您可以通过kill
发送信号)I would suggest a different approach:
I have a program that processes gigabytes of data and I want to see where I am in the process. This requires a large block of data to be printed, so I only want to do it when asked. I added a signal handler to
SIGTSTP
(the control-z keystroke sends this signal) which prints out the data. This does not require a separate thread, is simple to implement, and also allows you to signal it if you start the process elsewhere (since you can just send a signal viakill
)基本问题是,一旦您在后台尝试从 stdin 读取数据,您就会收到 SIGSTOP,这意味着就好像您立即再次按下了 ctrl-z 一样。如果您需要在后台运行,这里一个相对简单的更改是从 fifo 读取而不是从 stdin 读取。使用 mkfifo 命令创建一个众所周知的 fifo,如下所示:
像这样更改主循环:
读取将阻塞,直到写入某些内容。如果您计划同时运行多个实例,则必须小心。一旦从 fifo 中读取了一个项目,它就消失了。只有一个实例会看到您写入的值。您可以像“echo 1 >> fib.fifo”一样简单地写入文件
The basic problem is that as soon as you try to read from stdin when you are in the background, you will get SIGSTOP, which means it is as if you had hit ctrl-z again immediately. If you need to run in the background, a relatively simlpe change here is to read from a fifo instead of reading from stdin. Use the mkfifo command to create a well knownfifo like so:
The change your main loop like so:
The reads will block until something is written. You have to be careful here if you plan to run multiple instances of this at the same time. Once an item is read from a fifo, it is gone. Only one instance will see the value you have written. You can write to the file as simply as "echo 1 >> fib.fifo"
您可以通过阻止或处理
SIGTTIN
(以及类似的用于写入终端的SIGTTOU
)来阻止程序在后台尝试从终端读取数据时停止。当您的程序处于后台时,当它从终端读取或写入终端时,会分别传送
SIGTTIN
和SIGTTOU
。默认情况下,您的程序在收到这些信号时会停止。以下代码块使用
pthread_sigmask()
来阻止SIGTTIN
和SIGTTOU
(因为您的程序使用线程 - 否则sigprocmask()< /code> 可以使用):
上述操作应该在创建线程之前完成,因为新线程继承信号掩码。这将允许您的程序在写入和(尝试)从控制台读取时保持运行。
You can prevent the program from stopping when it tries to read from the terminal while backgrounded by blocking or handling
SIGTTIN
(and similarlySIGTTOU
for writing to the terminal).While your program is in the background,
SIGTTIN
andSIGTTOU
are delivered when it reads from or writes to the terminal, respectively. By default, your program gets stopped when it receives these signals.The following bit of code blocks
SIGTTIN
andSIGTTOU
usingpthread_sigmask()
(since your program uses threads -- otherwisesigprocmask()
could be used):The above should be done before you create your threads, since new threads inherit the signal mask. This will allow your program to keep running while writing to and (attempting to) read from the console.