Note the call to exit made by the parent thread in the main block. If the parent thread executes the exit call prior to either of the child threads executing printf, no output will be generated at all. Any thread, parent or child, who calls exit can terminate all the other threads along with the process. Thus our little hello world program has two race conditions. The race for the exit call and the race to see which child reaches the printf call first.
Lets fix the race conditions with a little crazy glue and duct tape.
Since we want each child thread to finish before the parent thread, lets insert a delay in the parent that will give the children time to reach printf. It is never safe to rely on timing delays to perform synchronization. Because threads are so tightly coupled its tempting to approach them with a less rigorous attitude concerning synchronization, but that temptation must be avoided. The race condition here is exactly the same situation we have with a distributed application and a shared resource. The resource is the standard output and the distributed computing elements are the three threads.