Programming multithreaded programs requires extra care and more discipline than programming conventional programs. The reason is that debugging multithreaded programs remains an art rather than a science, despite more than 30 years of research. Generally, avoiding errors is likely to be more effective than debugging them. Over the years a culture has developed with the following guidelines in programming with threads. Adhering to these guidelines will ease the process of producing correct programs:
 

   1.  All threads  share  heap data. This requires you to use proper synchronization primitives whenever two threads modify or read the shared data. Sometimes, it is obvious how to do so:
          char a[1000]
          void Modify(int m, int n)
          {
                 a[m + n] = a[m] + a[n];                   // ignore bound checks
          }
     If two thread will be executing this function, there is no guarantee that both will perceive consistent values of the members of the array a. Therefore, such a statment has to be protected by a mutex that ensures the proper execution as follows:
          char a[1000]
          void Modify(int m, int n)
          {
                Lock(p);
                a[m + n] = a[m] + a[n];                // ignore bound checks
                Unlock(p);
          }
     where p is a synchronization variable.

   2. Beware of the hidden data structures! While your own variables on the heap must be protected, it is also necessary to ensure that data structures belonging to the libraries and runtime system also be protected. These data structures are allocated on the heap, but you do not see them. Access to library functions can create situations where two threads may corrupt the data structures because proper synchronization is lacking. For example, two threads calling the memory allocator simultaneously through the malloc() library call might create problems if the data structures of malloc() are not designed for concurrent access. In that case,  data structures used to track the free space might be corrupted. The solution is to use a thread-safe version of libc.

For this reason, it is important to compile your program with the -D_POSIX_PTHREAD_SEMANTICS flag, so that the compiler knows to use the "thread safe" (synchronized) versions of libraries.

Note that not all libraries offer thread-safe versions.  Some library calls may only be used with single threaded programs. Before using a library call, you should find out whether it supports multithreaded use. In Solaris, you do this by looking at the man page for that library function. The man page will indicate that the call is "Thread Safe" (allows concurrent access by different threads assuming you have compiled with -D_POSIX_PTHREAD_SEMANTICS ), "Safe" (uses a lock to avoid concurrency within the call so that multithreaded programs may safely use it at some performance cost), and "Unsafe" (you can't use the call from a multi-threaded program).

 3. Simplicity of the code is also an important factor in ensuring correct operation. Complex pointer manipulations  may lead to errors and a runaway pointer may start corrupt the stacks of various threads, and therefore manifesting its presence through a set of incomprehensible bugs. Contrived logic, and multiple recursions may cause the stacks to overflow. Modern computer languages such as Java eliminate pointers altogether, and perform the memory allocation and deallocation by automatic memory management and garbage collection techniques. They simplify the process of writing programs in general, and multithreaded programs in particular. Still, without understanding of all the pitfalls that come with programming multithreaded applications, even the most sophistica ted programmer using the most sophisticated language may fall prey to some truly strange bugs.