From:http://hi.baidu.com/_wang_wei/blog/item/02c77a454eadc72fcffca376.html

线程的同步,发生在多个线程共享相同的内存的时候,这时要保证每个线程在每个时刻看到的共享数据是一致的。
如果每个线程使用的变量都是其他线程不会使用的(read&write),
或者变量是只读的,就不存在一致性问题。
但是,如果两个或两个以上的线程可以read/write一个变量时,就需要对线程进行同步,
以确保它们在访问变脸时,不会得到无效的值,同时也可以唯一地修改该变量并使它生效。
    以上就是我们所说的线程同步。
    线程同步有三种常用的机制:互斥量(mutex),读写锁(rwlock)和条件变量(cond)。
    互斥量有两种状态:lock和unlock,它确保同一时间只有一个线程访问数据。
    读写锁有三种状态:读加锁、写加锁、不加锁,只有一个线程可以占有写模式的读写锁,
但是可以有多个线程同时占有读模式的读写锁。
    条件变量则给多个线程提供了一个会合的场所,与互斥量一起使用时,
允许线程以无竞争的方式等待特定条件的发生。
互斥量:
    互斥量从本质上说就是一把锁,提供对共享资源的保护访问。
1、初始化:
    在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化:
对于静态分配的互斥量,可以把它设置为PTHREAD_MUTEX_INITIALIZER,或者调用pthread_mutex_init.
对于动态分配的互斥量,在申请内存(malloc)之后,通过pthread_mutex_init进行初始化,
并且在释放内存(free)前需要调用pthread_mutex_destroy。
原型:
    int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restric attr);
    int pthread_mutex_destroy(pthread_mutex_t * mutex);
头文件:<pthread.h>
返回值:成功则返回0,出错则返回错误编号。
说明:如果使用默认的属性初始化互斥量,只需要把attr设为NULL。其他值在以后讲解。
2、互斥操作:
    对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,
调用线程会阻塞,直到互斥量被解锁。
在完成了对共享资源的访问后,要对互斥量进行解锁。
首先说一下加锁函数:
头文件:<pthread.h>
原型:
    int pthread_mutex_lock(pthread_mutex_t * mutex);
    int pthread_mutex_trylock(pthread_mutex_t * mutex);
返回值:成功则返回0,出错则返回错误编码。
说明:具体说一下trylock函数,这个函数是非阻塞调用模式,也就是说,如果互斥量没被锁住,trylock函数将把
互斥量加锁,并获得对共享资源的访问权限;如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回
EBUSY,表示共享资源处于忙状态。
再说一下解锁函数:
头文件:<pthread.h>
原型:int pthread_mutex_unlock(pthread_mutex_t * mutex);
返回值:成功则返回0,出错则返回错误编码。
3、死锁:
    死锁主要发生在有多个依赖锁存在时,会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生。
如何避免死锁是使用互斥量应该格外注意的东西。
    总体来讲,有几个不成文的基本原则:
    对共享资源操作前一定要获得锁。
    完成操作以后一定要释放锁。
    尽量短时间地占用锁。
    如果有多锁,如获得顺序是ABC连环扣,释放顺序也应该是ABC。
    线程错误返回时应该释放它所获得的锁。
读写锁
    在线程同步系列的第一篇文章里已经说过,读写锁是因为有3种状态,所以可以有更高的并行性。
1、特性:
    一次只有一个线程可以占有写模式的读写锁,但是可以有多个线程同时占有读模式的读写锁。正式因为这个特性,
    当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。
    当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,
但是如果线程希望以写模式对此锁进行加锁,它必须阻塞知道所有的线程释放锁。
    通常,当读写锁处于读模式锁住状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式
锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞。
2、适用性:
    读写锁适合于对数据结构的读次数比写次数多得多的情况。因为读模式锁定时可以共享,以写模式锁住时意味
着独占,所以读写锁又叫共享-独占锁。
3、初始化和销毁:
    #include <pthread.h>
    int pthread_rwlock_init(pthread_rwlock_t * restrict rwlock, const pthread_rwlockattr_t * restrict attr);
    int pthread_rwlock_destroy(pthread_rwlock_t * rwlock);
成功则返回0,出错则返回错误编码。
同互斥量以上,在释放读写锁占用的内存之前,需要先通过pthread_rwlock_destroy对读写锁进行清理工作,
释放由init分配的资源。
4、读和写:
    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t * rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t * rwlock);
成功则返回0,出错则返回错误编码。
这3个函数分别实现获取读锁,获取写锁和释放锁的操作,获取锁的两个函数是阻塞操作,
同样,非阻塞函数为:
    #include <pthread.h>
    int pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock);
成功则返回0,出错则返回错误编号。
非阻塞的获取锁操作,如果可以获得则返回0,否则返回错误的EBUSY。
 
条件变量
    条件变量分为两部分:条件和变量。条件本身是由互斥量保护的。线程在改变条件状态前先要锁住互斥量。
1、初始化:
    条件变量采用的数据类型是pthread_cond_t,在使用之前必须要进行初始化,这包括两种方式:
    静态:可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量。
    动态:pthread_cond_init函数,是释放动态条件变量的内存空间之前,要用pthread_cond_destroy对其进行清理。
    #include <pthread.h>
    int pthread_cond_init(pthread_cond_t * restrict cond, pthread_condattr_t * restrict attr);
    int pthread_cond_destroy(pthread_cond_t * cond);
成功则返回0,出错则返回错误编号。
当pthread_cond_init的attr参数为NULL时,会创建一个默认属性的条件变量;非默认情况以后讨论。
2、等待条件:
    #include <pthread.h>
    int pthread_cond_wait(pthread_cond_t * restrict cond, pthread_mutex_t * restrict mutex);
    int pthread_cond_timedwait(pthread_cond_t * restrict cond, pthread_mutex_t * restrict mutex, 
    const struct timespec * restrict timeout);
成功则返回0,出错则返回错误编号。
    这两个函数分别是阻塞等待和超时等待。
    等待条件函数等待条件变为真,传递给pthread_cond_wait的互斥量对条件进行保护,
调用者把锁住的互斥量传递给函数。函数把调用线程放到等待条件的线程列表上,
然后对互斥量解锁,这两个操作是原子的。这样便关闭了条件检查和线程进入休眠状态
等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。
    当pthread_cond_wait返回时,互斥量再次被锁住。
3、通知条件:
    #include <pthread.h>
    int pthread_cond_signal(pthread_cond_t * cond);
    int pthread_cond_broadcast(pthread_cond_t * cond);
成功则返回0,出错则返回错误编号。
    这两个函数用于通知线程条件已经满足。调用这两个函数,也称向线程或条件发送信号。
必须注意,一定要在改变条件状态以后再给线程发送信号。