Question
Write a program whose main function creates a second thread and then use a global variable to have the two threads ( main is always
Write a program whose main function creates a second thread and then use a global variable to have the two threads ( main is always the starting point of the first thread, no?) communicate with each other. In main, after the (successful) call to pthread_create, have main prompt the user for a non-zero integer and store that non-zero value in the global variable so that the second thread can see it (since all threads of a process share the same heap and global sections). Then have main spin while the global variable is not zero i.e., until something external to the main thread (like the second thread) sets the global variable back to zero. Spin? A thread is said to spin when it deliberately enters an apparently infinite loop until the hardware or some other process or thread, executing concurrently, changes the condition so that the spinning thread can exit its loop (which therefore wasn't really infinite after all, but it lookslike it could go on forever, and in the absence of concurrency, it could and would). In the second thread, after it prints out its TID, have the second thread spin while the global varable is 0 thus "waiting" (technically, "busy waiting") to receive the non-zero value from the main thread. Once the second thread stops spinning, have it print out the value that caused it to exit from the loop and then set the global variable back to 0 so the main thread can exit its spin and terminate. This busy-waiting is, of course a very inefficient method of synchronizing our concurrent threads. Later we'll look at better ways. Anyway, here's a sample printout, below.
Main: PID is 1915 Main: Successfully created a thread with TID of 1101519168Al Main: Enter a non-zero integer so that the other thread can get out of its spin loop: Second thread: My TID is 1101519168Al, still in process #1915; I'm about to spin until main enters a non-zero value into the shared variable. 56 Second thread: Main set the shared variable to 56; I'll set it back to zero and then I'm done. Main: Second thread has re-zeroed the shared variable, so I'm done.
Note that the user's input of 56, seems oddly placed in the printout, above. Can you figure out why? main printed the prompt for user input, but then had to wait for me to type in a number. Well, compared to computers, people are pretty slow so the second thread, executing concurrently (and it would be concurrent even on a single CPU system where it was not simultaneous), got a chance to execute while the main thread was in the wait state, waiting for my keyboard input to reach main's scanf. The second thread thus printed out some of its stuff before the user (me) got around to typing in a number for the main thread. So the second thread's output showed up on my screen after the prompt from main but before I had typed in my stuff in response to main's prompt. Resources (such as keyboards and displays) are owned by the process and shared by all threads within a process so unless we do something special, printouts and keyboard requests from multiple threads within the same process can easily get jumbled up, just as they did, above potentially a real problem. Technically, this odd printout is a race condition --- the shared resource is the output buffer and hence the screen itself. Although in this case, it is a very predicatabe race condition --- humans being much slower than computers, we're always going to see the second thread's first output before we see the user's input. But if the human being were as fast as a computer, sometimes the user's input would show up "properly", before the second thread's first output, sometimes it wouldn't --- that's what the concurrency means. And when, as it did here, we got the ouput on the screen in the "wrong" place, we'd have the race condition we see here. In this case, of course, the race condition can't kill anybody, it just makes the output look weird.
Given that you got your last program working (homework #2), the code here is not going to be much trouble for you; the point of this assignment is that, unlike the first thread homework (homework #2 overall), here we can see a definite demonstration of concurrency: If the two threads were not running concurrently, the second thread would probably start to run while the main thread waited for your user input (as it did in the sample printout, above). So the second thread would start executing and immediately enter into its spin-loop, where it would remain forever, since, in the absence of concurrency among our threads, the first thread would never get to run again to set the global variable to your user-entered value, which is the only thing that can cause the second thread to stop spinning. So the fact that these two threads did communicate and the process did terminate normally demonstrates that the threads were executing concurrently, communicating via a global variable. Given that prclab is a symmetric multiprocessor, my code's two threads were probably even (briefly) running simultaneously on two separate CPU's. But this code would work exactly the same way even on a single CPU system as long as there was a way for the two threads to execute concurrently. Remember: Concurrently does not mean simultaneously. When we study CPU scheduling (chapter 6) we'll see why some short term schedulers allow concurrency even on a single CPU (we talked about that when I defined concurrency for you) and then talk about which types of short term scheduling allow concurrency on even a single CPU and which don't.
Notes:
Make sure to initialize the gobal variable to 0 so that the second thread correctly detects when the main thread has received the user's input (because the flag, which was initialized to 0, has become non-zero). If the global were not initialized to 0, the second thread would never spin and would just printout whatever the uninitialized global variable happened to contain. Sounds like a race condtion to me, since the value printed by the second thread would be incorrect because concurrent execution allowed it to execute its garbage printout before the first thread had stored the correct value in the global variable. If the second thread is not dispatched (and hence does not try to start spinning) until after the first thread reads and stores the correct value, everything will work fine. But concurrency means we that we can't predict which thread will do what first, so without the initialization of the gloabl to 0, the result of the computation (the printed output, in this case) would depend on the actual order of interleave of the concurrent execution of two threads, which is the definition of a race condtion In this assignment, simply initializing the global to 0 prevents the race condition; but in general, it won't be that simple. We'll deal with a more general case on the next programming assignment.
Do not use pthread_join in this assignment; the only way the main thread will know the second thread is done is when the global variable is re-set to 0.
Make sure to check for success or failure of pthread_create and use strerror to tell you what went wrong if you were unsuccessful. From now on (forever), use either perror or strerror, as appropriate, for every system service call your code ever makes. And remember, to use strerror, you need to #include
You don't need to use pthread_exit or anything like it in this assignment (nothing but pthread_create, in fact). If it hasn't explicitly terminated itself earlier, a thread is terminated by executing a return from the function it started in and a C compiler always places a return code at the very end of a function if the programmer doesn't do so explicitly. You've probably relied on that compiler-inserted return for years to terminate your programs when they reach the end of main . When a thread returns from its starting function, who does it return to? Can't be another of your functions, since it (the first function of the thread) was not "called" by one of your functions in the normal C sense of the term "called". It must therefore return to the OS which created it, which then terminates it. There's nothing actually special about main, by the way. It's just the function the OS starts the first thread of a process executing in. Some languages provide a way for the programmer to tell the compiler explicitly which function the first thread is to start executing in; but the C language and its compiler(s), designed for simplicity, don't include the complexity involved in giving programmers any choice here (i.e., the starting function). To be compiled successfully by a C compiler, every complete program must have a main function because that's where the C compiler is going to tell the the OS to start the execution of the first thread in the process. But when your code creates a thread (as opposed to the compiler's creation of the main thread) you must explicitly specify the starting function, and when that function executes a return (to the OS), the OS terminates the thread. And of course if that thread happens to be the only thread in the process, the OS terminates the process too (since every process must contain at least one thread) and reclaims all its resources (e.g., memory).
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started