26-29 An alarm thread is created, running function alarm_thread, with the alarm data (alarm_t) as the thread's argument.
■ alarm_thread.c_ part 3 main
1 int main (int argc, char *argv[])
2 {
3 int status;
4 char line[128];
5 alarm_t *alarm;
6 pthread_t thread;
7
8 while (1) {
9 printf ("Alarm> ");
10 if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
11 if (strlen (line) <= 1) continue;
12 alarm = (alarm_t*)malloc (sizeof (alarm_t));
13 if (alarm == NULL)
14 errno_abort ("Allocate alarm");
15
16 /*
17 * Parse input line into seconds (%d) and a message
18 * (%64[^\n]), consisting of up to 64 characters
19 * separated from the seconds by whitespace.
20 */
21 if (sscanf (line, "%d %64[^\nJ",
22 &alarm->seconds, alarm->message) < 2) {
23 fprintf (stderr, "Bad command\n");
24 free (alarm);
25 } else {
2 6 status = pthread_create (
27 &thread, NULL, alarm_thread, alarm);
28 if (status != 0)
29 err_abort (status, "Create alarm thread");
30 }
31 }
32 }
1.5.4 Summary
A good way to start thinking about threads is to compare the two asynchronous versions of the alarm program. First, in the fork version, each alarm has an independent address space, copied from the main program. That means we can put the seconds and message values into local variables—once the child has been created (when fork returns), the parent can change the values without affecting the alarm. In the threaded version, on the other hand, all threads share the same address space—so we call malloc to create a new structure for each alarm, which is passed to the new thread. The extra bookkeeping required introduces a little complexity into the threaded version.
In the version using fork, the main program needs to tell the kernel to free resources used by each child process it creates, by calling waitpid
or some other member of the wait "family." The alarm_fork.c program, for example, calls waitpid
in a loop after each command, to collect all child processes that have completed. You do not need to wait for a thread unless you need the thread's return value — in alarm_thread.c, for example, each alarm thread
In the threaded version, the "primary activities" (sleeping and printing the message) must be coded in a separate routine. In alarm.c and alarm_fork.c, those activities were performed without a call. In simple cases such as our alarm program, it is often easier to understand the program with all code in one place, so that might seem like an advantage for alarm_fork .c. In more complicated programs, though, it is rare that a program's "primary activities" are so simple that they can be performed in a single routine without resulting in total confusion.
In a real alarm program, you wouldn't want to create a process for each alarm. You might easily have hundreds of alarms active, and the system probably wouldn't let you create that many processes. On the other hand, you probably can create hundreds of threads within a process. While there is no real need to maintain a stack and thread context for each alarm request, it is a perfectly viable design.
1.6 Benefits of threading