开发使用多线程过程中, 不可避免的会出现多个线程同时操作同一块共享资源, 当操作全部为读时, 不会出现未知结果, 一旦当某个线程操作中有写操作时, 就会出现数据不同步的事件. 

    而出现数据混乱的原因:


    • 资源共享(独享资源则不会)

    • 调试随机(对数据的访问会出现竞争)

    • 线程间缺少必要的同步机制   

    以上三点, 前两点不能被改变. 欲提高效率, 传递数据, 资源必须共享. 只要资源共享, 就一定会出现线程间资源竞争, 只要存在竞争关系, 数据就会出现混乱.

    所以只能从第三点着手, 使多个线程在访问共享资源的时候, 出现互斥.


线程同步:

    指在一定的时间里只允许某一个进程访问某个资源,而在此时间内,不允许其它线程对该资源进行操作.


    线程的同步机制:


    • 互斥量(互斥锁)

    • 读写锁

    • 条件变量(需要配合互斥量来使用)

    • 信号量        

    

    互斥量(互斥锁)

    每个线程对资源操作前都需要拿到"锁", 成功加锁后才能操作,操作完解锁后,其它线程才能拿锁对该资源进行操作, 如果一个线程在此先拿到锁, 那么拿锁操作会阻塞, 直接拿到锁的线程进行解锁操作.

    常用API

    初始化: pthread_mutex_init(pthread_mutex_t*restrict mutex, const pthread_mutexattr_t *restrict attr);

    静态初始化: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


    释放: pthread_mutex_destroy(pthread_mutex_t *mutex);


    restrict: C99标准类型限定符, 用于告诉编译器, 该指向的对象已被引用, 不能被除该指针外的所有直接或间接的方法修改该指向的内容.


    加锁: pthread_mutex_lock(pthread_mutex_t *mutex);  

  

    尝试加锁: pthread_mutex_trylock(pthread_mutex_t* mutex);


    解锁: pthread_mutex_unlock(pthread_mutex_t* mutex);


    pthread_mutex_lock和pthread_mutex_trylock的区别在于pthread_mutex_lock如果没有获取到锁会阻塞等待, 而pthread_mutex_trylock没有获取到锁会立即返回错误号(EBUSY).


    直接上案例: [两个线程有顺序的打印hello wolrd和HELLO WOLRD]

    

#include 
#include 
#include 
#include 
#include 
#include 
//全局定义互斥锁
pthread_mutex_t mutex;

//子线程打印hello world
void* tfn(void* arg){
	srand(time(NULL));
	while(1){
		//加锁
		pthread_mutex_lock(&mutex);		
		printf("hello");
		//模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误
		sleep(rand()%3);
		printf("world\n");
		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(2);
	}
}
int main(int argc, char* argv[]){
	pthread_t tid;
	//随机种子
	srand(time(NULL));
	//互斥锁初始化
	pthread_mutex_init(&mutex, NULL);
	//创建子线程
	pthread_create(&tid, NULL, tfn, NULL);
	//父线程打印HELLO WOLRD
	while(1){
		//加锁
		pthread_mutex_lock(&mutex);
		printf("HELLO");
		//模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误
		sleep(rand()%3);
		printf("WORLD\n");
		//解锁
		pthread_mutex_unlock(&mutex);
		sleep(2);
	}
	return 0;
}

    

    读写锁:

   与互斥锁类似, 但读写锁允许更高的并行性.其特定为: 写独占, 读共享. 在读写锁竞争时, 写锁优先级高.

   常用API

    初始化: pthread_rwlock_init(pthread_rwlock_t*restrict rwlock, const pthread_mutexattr_t *restrict attr);


    释放: pthread_rwlock_destroy(pthread_rwlock_t *rwlock);


    restrict: C99标准类型限定符, 用于告诉编译器, 该指向的对象已被引用, 不能被除该指针外的所有直接或间接的方法修改该指向的内容.


    读加锁: pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

    尝试读加锁: pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

    写加锁: pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 

    尝试写加锁: pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 

  

    解锁: pthread_rwlock_unlock(pthread_rwlock_t* mutex);

    直接上案例: [3个线程不定时写同一全局资源, 5个线程不定时读同一全局资源.]

#include 
#include 
#include 
 
int counter;    //共享资源
pthread_rwlock_t rwlock;    
void *th_write(void *arg)
{
    int t, i = (int)arg;
    while (1) {
        //获取写锁
        pthread_rwlock_wrlock(&rwlock);
        t = counter;
        usleep(1000);
       printf("=======write %d: %lu: counter=%d ++counter=%d\n", i,pthread_self(), t, ++counter);
       //解锁
       pthread_rwlock_unlock(&rwlock);
        usleep(10000);
    }
    return NULL;
}
void *th_read(void *arg)
{
    int i = (int)arg;
 
    while (1) {
        //获取读锁
       pthread_rwlock_rdlock(&rwlock);
       printf("----------------------------read %d: %lu: %d\n", i,pthread_self(), counter);
       //解锁
       pthread_rwlock_unlock(&rwlock);
        usleep(2000);
    }
    return NULL;
}
int main(void)
{
    int i;
    pthread_t tid[8];
    //初始化读写锁
   pthread_rwlock_init(&rwlock, NULL);
     
     //创建3个写线程
    for (i = 0; i < 3; i++)
       pthread_create(&tid[i], NULL, th_write, (void *)i);
    //创建5个读线程
    for (i = 0; i < 5; i++)
        pthread_create(&tid[i+3],NULL, th_read, (void *)i);
    //等待回收所有线程
    for (i = 0; i < 8; i++)
        pthread_join(tid[i],NULL);
     //释放读写锁
    pthread_rwlock_destroy(&rwlock);
    return 0;
}