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

    而出现数据混乱的原因:


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

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

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

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

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


线程同步:

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


    线程的同步机制:


    • 互斥量(互斥锁)

    • 读写锁

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

    • 信号量        

    

    互斥量(互斥锁)

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

    常用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]

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
//全局定义互斥锁
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个线程不定时读同一全局资源.]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
  
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;
}