线程同步问题和互斥锁mutex

我们通过下例程序,引出线程同步的概念:
该代码功能为两个线程不断向STDOUT打印hello world,子线程是小写的,主线程是大写的

 #include<stdio.h>
 #include<string.h>
 #include<pthread.h>
 #include<stdlib.h>
 #include<unistd.h>
 
 void *tfn(void *arg)
 {
     srand(time(NULL));
     while(1)
     {
      	 printf("hello ");
         sleep(rand()%3);//模拟长时间操作共享内存,导致cpu易主,产生与时间有关的错误
         printf("world\n");
         sleep(rand()%3);
     }
     return NULL;
 }
 int main(void)
 {
     //父线程和子线程的共享资源为STDOUT
 
     pthread_t tid;
     srand(time(NULL));
 
     pthread_create(&tid,NULL,tfn,NULL);
     while(1)
     {
         printf("HELLO ");
         sleep(rand()%3);
         printf("WORLD\n");
         sleep(rand()%3);
     }
     return 0;
 }

运行结果:
在这里插入图片描述
可以看到打印出来是混乱的,这是因为子线程可能打印了一半后,时间片轮完阻塞等待cpu,然后主控线程得到了cpu的使用权,接着往屏幕打印,所以看起来是混乱的,这里就涉及到线程同步问题了。

类似于上述例子,当多个线程来访问同一共享资源时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题。

**同步机制就是:通过一些机制实现对共享资源的有序访问。即多个线程按一种规则来对共享资源顺序访问(在对共享资源操作时,必须保证没有其他线程操作,若在访问共享数据时有其他线程正在对其操作,则需要等待其操作完成再访问)。**这里可以看这篇博客点击此处,里面讲的很清楚。

我们通过互斥锁来解决上述代码的问题:
互斥锁的数据类型为pthread_mutex_t,是一个结构体,我们可以把它当作值只有0和
1的数来简化看待,为1时状态为加锁状态,0为不加锁状态。
我们对互斥锁的操作都需要调用函数来进行,有以下函数:

// 初始化互斥锁
int pthread_mutex_init (pthread_mutex_t * restrict mutex , \
                        const pthread_mutexattr_t * restrict attr ) ;
//销毁锁
int pthread_mutex_destroy (pthread_mutex_t * mutex ) ;
// 加锁
int pthread_mutex_lock (pthread_mutex_t *mutex) ;
// 解锁
int pthread_mutex_unlock (pthread_mutex_t *mutex) ;
// 尝试加锁
int pthread_mutex_trylock (pthread_mutex_t *mutex) ;

我们把对共享资源操作的代码称之为临界区,一般再进入临界区之前会加锁,离开临界区后会解锁。若加锁的时候这把锁已经被别的线程占有(即已经有线程加锁了),则该线程会阻塞等待,当加锁的线程操作完成解锁后,会唤醒阻塞在锁上的进程,这样就实现了线程间的同步。但是需要注意的是,所有的锁机制都是一把建议锁,也就是操作系统给你提供了锁机制,但并不强制要求。比如说一个线程已经加锁了,这个时候另一个线程没有加锁就对共享资源访问也是会成功的,操作系统并不会因为你加了锁就会限制其他线程访问共享资源。

使用方法:在全局建一把锁,在主控线程中对互斥锁初始化,在所有临界区前后加锁,解锁。

下面来对开头的代码引入互斥锁解决bug,来说明互斥锁的应用:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<pthread.h>
  4 #include<stdlib.h>
  5 #include<unistd.h>
  6 
  7 void *tfn(void *arg)
  8 {   
  9     srand(time(NULL));
 10     
 11     while(1)
 12     {   
 13         pthread_mutex_lock((pthread_mutex_t *)arg);//加锁
 14         
 15         printf("hello ");
 16         sleep(rand()%3);//模拟长时间操作共享内存,导致cpu易主,产生与时间有关的错误
 17         printf("world\n");
 18         
 19         pthread_mutex_unlock((pthread_mutex_t *)arg);//解锁
 20         sleep(rand()%3);
 21     }
 22     return NULL;
 23 }
 24 int main(void)
 25 {
 26     //父线程和子线程的共享资源为STDOUT
 27 
 28     pthread_t tid;
 29     srand(time(NULL));
 30 
 31     pthread_mutex_t mutex;//建锁
 32     pthread_mutex_init(&mutex,NULL);//初始化锁
 33     pthread_create(&tid,NULL,tfn,(void*)&mutex);//一般做法是把mutex设为全局变量,这里设成局部变量通过传参也是可以的
 34     while(1)
 35     {
 36         pthread_mutex_lock(&mutex);//加锁
 37         
 38         printf("HELLO ");
 39         sleep(rand()%3);
 40         printf("WORLD\n");
 41 
 42         pthread_mutex_unlock(&mutex);//解锁
 43 
 44         sleep(rand()%3);
 45     }
 46 
 47     pthread_mutex_destroy(&mutex);//释放锁
 48     return 0;
 49 }

在这里插入图片描述

发布了31 篇原创文章 · 获赞 4 · 访问量 951

猜你喜欢

转载自blog.csdn.net/qq_39781096/article/details/104425804
今日推荐