Linux——线程同步

前言

当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图,若每个线程使用的变量都是其他线程不会读取或修改的,那么就不存在一致性概念,同样地,若变量是只读的,多个线程同时读取该变量也不会有一致性问题,但是当某个线程可以修改变量,而其他线程也可以读取或者修改这个变量的时候,就需要对线程进行同步,以确保它们在访问变量的存储内容时不会访问到无效的数值。

线程同步的概念

线程同步指的是当一个线程在对某个临界资源进行操作时,其他线程都不可以对这个资源进行操作,直到该线程完成操作,其他线程才能操作,也就是协同步调,让线程按预定的先后次序进行运行,线程同步的方法有四种:

  • 互斥锁
  • 信号量
  • 条件变量
  • 读写锁

互斥锁

概念
互斥锁只有两种状态,加锁状态和解锁状态,
如果一个线程对已经处于加锁状态互斥锁进行加锁操作,则枷锁操作会阻塞,直到正在对互斥锁加锁状态线程进行解锁操作。
互斥锁的接口

#include

pthread_mutex_t  //互斥锁的类型

初始化互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);

加锁操作

int pthread_mutex_lock(pthread_mutex_t *mutex)

尝试加锁,不会阻塞

int pthread_mutex_trylock(pthread_mutex_t *mutex)

解锁操作

int pthread_mutex_unlock(pthread_mutex_t *mutex)

销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex)

互斥锁的使用
示例:模拟连个线程竞争一个打印机,A线程使用打印机输出一个a,使用完成后输出一个a,B线程也一样,这样我们的输出结果不会出现abab/baba
如何让两个线程操作的是同一把互斥锁:将锁定义到全局

#include
#include
#include
#include
#include
#include

#include

pthread_mutex_t mutex;

void *threadFun(void *arg) //B线程
{
	int i = 0;
	for(;i < 5;i++)
	{
		pthread_mutex_lock(&mutex);
		printf("B");
		fflush(stdout);
		int n = rand() % 3;
		sleep(n);

		printf("B");
		fflush(stdout);
		pthread_mutex_unlock(&mutex);

		n = rand() % 3;
		sleep(n);
	}	
}

void threadMain() //A线程
{
	int i = 0;
	for(;i < 5;i++)
	{
		pthread_mutex_lock(&mutex);
		printf("A");
		fflush(stdout);
		int n = rand() % 3;
		sleep(n);

		printf("A");
		fflush(stdout);
		pthread_mutex_unlock(&mutex);

		n = rand() % 3;
		sleep(n);
	} 
}

int main()
{
	srand((unsigned int)time(NULL));

	pthread_mutex_init(&mutex,NULL);

	pthread_t id;
	int res = pthread_create(&id,NULL,threadFun,NULL);
	assert(res == 0);

	threadMain();

	pthread_join(id,NULL);
	pthread_mutex_destroy(&mutex);
	exit(0);
}

执行结果
在这里插入图片描述

信号量

概念
线程级信号量和进程级信号量的原理是相同的,信号量也是特殊的计数器,当值>0时,记录的是临界资源的个数,=0时,表示没有临界资源可用,这时对信号量执行P操作,则线程会被阻塞。
信号量的接口

#include

sem_t //线程级信号量的类型

初始化信号量并且给定初始值

int sem_init(sem_t *sem,int shared,int val);

对信号量执行P操作

int sem_wait(sem_t *sem);

对信号量执行V操作

int sem_post(sem_t *sem);

销毁信号量

int sem_destroy(sem_t *sem);

信号量和互斥锁的区别

  • 互斥锁只有两种状态,信号量的值可以大于1
  • 在一个线程中对互斥锁的加锁和解锁必须成对出现,信号量可以在线程间混用

读写锁

概念
读写锁在互斥锁的基础上,允许一个更高的并行性,读写锁一共有三种状态:

  • 解锁状态
    任何线程任何方式加锁都可以成功。
  • 读加锁状态
    一个线程对锁执行读加锁,则可以成功,但是一个线程如果执行写加锁,则会被阻塞,直到所有读加锁的线程都执行了解锁操作。
  • 写加锁状态
    一个线程只要对锁已经写加锁,所有的加锁操作都会被阻塞。

读写锁的接口

#include

pthread_rwlock_t;  //读写锁的类型

初始化读写锁

int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlock_t *attr);

读加锁操作

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)

写加锁操作

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)

解锁操作

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)

销毁互斥锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);   

自旋锁

和互斥锁类似,只是当加锁操作被阻塞,阻塞的方式不同:互斥锁是通过将线程睡眠,自旋锁则是通过忙等待的方式。
自旋锁一般适用的场景是锁被其他线程短期持有(很快会被释放),而且等待该锁的线程不希望在阻塞期间被取消调度,因为这会带来一些开销。

条件变量

概念
条件变量给多个线程提供了一个会和的场所,条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件的发生。
条件本身是由互斥量保护的,线程在改变条件状态前必须锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待
这个共享数据的线程。

条件变量的使用

#include 
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // 会阻塞, 传递的是加锁状态的锁 int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond);

代码示例

#include  
#include  
#include  
#include  
#include 
 #include 
 
pthread_mutex_t mutex;
pthread_cond_t cond;
 
char buff[128] = {0};

void *fun(void *arg)
{
	int flg = *(int*)arg;
	while(1)
	{
		 pthread_mutex_lock(&mutex);
		 pthread_cond_wait(&cond, &mutex);  //  mutex肯定是一个加锁状态的锁,以互斥方式将当前线程添加到等待条件变量的队列中        
		 pthread_mutex_unlock(&mutex);
		
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
		
 		printf("fun%d:%s\n",flag,buff);
 		memset(buff,0,128);
	}

	printf("fun over\n");
}
int main()
{
	 pthread_cond_init(&cond, NULL);
	 pthread_mutex_init(&mutex, NULL);
 
 	int flag1 = 1;
 	int flag2 = 2;
 	pthread_t id1, id2; 
 	pthread_create(&id1, NULL, fun, (void*)(&flag1));
 	pthread_create(&id2, NULL, fun, (void*)(&flag2));
 
 	  while(1)
 	  {       
 	  	 printf("input: ");
 	  	 fflush(stdout);
 	  	 fgets(buff, 127, stdin);
 	  	 
        if(strncmp(buff, "end", 3) == 0)
        {
	        pthread_mutex_lock(&mutex);
	        pthread_cond_broadcast(&cond);
	        pthread_mutex_unlock(&mutex);
	        break;
        }
        else
        {
        	pthread_mutex_lock(&mutex);
	        pthread_cond_signal(&cond);
	        pthread_mutex_unlock(&mutex);
        }
      }
      
     pthread_join(id1, NULL);
     pthread_join(id2, NULL);

	 pthread_mutex_destroy(&mutex);
	 pthread_cond_destroy(&cond);
 
  	 exit(0);
}

你可能感兴趣的:(Linux)