Read a good posix threads tutorial. You'll understand then why it is so important in practice to use synchronization primitives (at least to get understandable behavior from your program).
Any data can be shared between threads, because they all share the same common address space. However, you really want to synchronize such shared access (because you cannot know, without explicit synchronization, when one thread sees the changes done by another thread; read about cache coherence). A common way is to use mutexes for that.
To explain a bit, declare a global mutex with your shared global data:
pthread_mutex_t glob_mtx = PTHREAD_MUTEX_INITIALIZER;
static struct globaldata_st glob_data;
Then, to access some of the data, do e.g.
int sharednumber;
pthread_mutex_lock(&glob_mtx);
sharednumber = glob_data.number;
pthread_mutex_unlock(&glob_mtx);
And to atomically update that data, by incrementing it:
int sharednumber;
pthread_mutex_lock(&glob_mtx);
sharednumber = glob_data.number++;
pthread_mutex_unlock(&glob_mtx);
you would similarly serialize the update or access to a shared linked list queue, etc....
Don't be shy on using mutexes, they are quite fast. Always pair a pthread_mutex_lock
with a pthread_mutex_unlock
....
Remember that synchronization bugs are very hard to hunt because they are not reproducible: they are heisenbugs.
With GCC 4.8 on Linux/x86-64 you could use the thread sanitizer with gcc -Wall -fsanitize=thread -g
to ease debugging.