12.2 Use of Threads to Monitor Multiple File DescriptorsMultiple threads can simplify the problem of monitoring multiple file descriptors because a dedicated thread with relatively simple logic can handle each file descriptor. Threads also make the overlap of I/O and processing transparent to the programmer.We begin by comparing the execution of a function by a separate thread to the execution of an ordinary function call within the same thread of execution. Figure 12.1 illustrates a call to the processfd function within the same thread of execution. The calling mechanism creates an activation record (usually on the stack) that contains the return address. The thread of execution jumps to processfd when the calling mechanism writes the starting address of processfd in the processor's program counter. The thread uses the newly created activation record as the environment for execution, creating automatic variables on the stack as part of the record. The thread of execution continues in processfd until reaching a return statement (or the end of the function). The return statement copies the return address that is stored in the activation record into the processor program counter, causing the thread of execution to jump back to the calling program. Figure 12.1. Program that makes an ordinary call to processfd has a single thread of execution.![]() Figure 12.2 illustrates the creation of a separate thread to execute the processfd function. The pthread_create call creates a new "schedulable entity" with its own value of the program counter, its own stack and its own scheduling parameters. The "schedulable entity" (i.e., thread) executes an independent stream of instructions, never returning to the point of the call. The calling program continues to execute concurrently. In contrast, when processfd is called as an ordinary function, the caller's thread of execution moves through the function code and returns to the point of the call, generating a single thread of execution rather than two separate ones. Figure 12.2. Program that creates a new thread to execute processfd has two threads of execution.![]() We now turn to the specific problem of handling multiple file descriptors. The processfd function of Program 4.3 instead of read to restart reading if the thread is interrupted by a signal. However, we recommend a dedicated thread for signal handling, as explained in Section 13.5. In this case, the thread that executes processfd would have all signals blocked and could call read. Program 12.1 processfd.cThe processfd function monitors a single file descriptor for input.
Example 12.1The following code segment calls processfd as an ordinary function. The code assumes that fd is open for reading and passes it by reference to processfd.
Example 12.2The following code segment creates a new thread to run processfd for the open file descriptor fd.
The code of Program 4.14 and Program 4.17. The threaded version is considerably simpler and takes advantage of parallelism. If docommand causes the calling thread to block for some reason, the thread runtime system schedules another runnable thread. In this way, processing and reading are overlapped in a natural way. In contrast, blocking of docommand in the single-threaded implementation causes the entire process to block.If monitorfd fails to create thread i, it sets the corresponding thread ID to itself to signify that creation failed. The last loop uses pthread_join, described in Section 12.3, to wait until all threads have completed. Program 12.2 monitorfd.cA function to monitor an array of file descriptors, using a separate thread for each descriptor.
|