【Linux学习】- 线程同步 线程互斥量 线程信号量 操作

目录

一:线程同步

二:代码学习 线程同步

2.1 线程执行随机 执行顺序随机 执行时间随机 【CPU的轮片执行】

2.2 线程互斥量

2.3 线程信号量


一:线程同步

•两个(或多个)线程同时执行时,经常需要访问到【公共资源】或【代码的关键部分】,这时就涉及到了线程的同步问题,我们可以通过下面两种方法来更好地控制线程的执行情况和更好地访问代码的关键部分
•线程信号量
•线程互斥量

二:代码学习 线程同步

因为多个线程同时执行 而且还访问同样的数据 此时就产生了线程同步 线程同步归根结底就是数据的不安全 这个问题就必须要去解决。

2.1 线程执行随机 执行顺序随机 执行时间随机 【CPU的轮片执行】

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
using namespace std;

typedef struct student
{
	char stuname[20];
	char sexy[6];
}STU;

STU stu = { 0 };

void* pthread_function1(void* pv)
{
	int num = 0;
	while (1)
	{
		if (num % 2 == 0)
		{
			strcpy(stu.stuname, "陈茹涵");
			strcpy(stu.sexy, "男生");
		}
		else
		{
			strcpy(stu.stuname, "林夕梦");
			strcpy(stu.sexy, "女生");
		}
		num++;
		//sleep(1);//这里只是两个线程 如果很多线程 并且 不知道线程执行时间 这就完全不可行
	}
}

void* pthread_function2(void* pv)
{
	while (1)
	{
		cout  << "stu.stuname = "<<stu.stuname << "stu.sexy = " << stu.sexy << endl;
		//sleep(1);//这里只是两个线程 如果很多线程 并且 不知道线程执行时间 这就完全不可行
	}
}

int main()
{
	pthread_t threadid1 = 0;
	pthread_t threadid2 = 0;

	pthread_create(&threadid1, NULL, pthread_function1, NULL);
	pthread_create(&threadid1, NULL, pthread_function2, NULL);

	while (1)
	{

	}

	return 0;
}

结果:会出现姓名和性别不对应 

原来正确的是 陈茹涵 男生    林夕梦 女生

可是打印出来的会出现有 陈茹涵 女生 林夕梦 男生 的错误情境

原因就是:线程执行随机 执行顺序随机 执行时间随机 【CPU的轮片执行】

 

2.2 线程互斥量

 用互斥量进行同步

•使用互斥量是实现多线程程序中的同步访问的另一种手段
•程序员给某个对象加上一把“锁”,每次只允许一个线程去访问它
•如果想对代码关键部分的访问进行控制,你必须在进入这段代码之前锁定一把互斥量,在完成操作后再打开它

互斥量函数组

互斥量对象用pthread_mutex_t表示,相关函数都在<pthread.h>头文件里面声明

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
using namespace std;

typedef struct student
{
	char stuname[20];
	char sexy[6];
}STU;

STU stu = { 0 };

//互斥量
pthread_mutex_t mutex;

void* pthread_function1(void* pv)
{
	int num = 0;
	while (1)
	{
		if (num % 2 == 0)
		{
			pthread_mutex_lock(&mutex);//加锁 阻塞式函数 只有非加锁状态才可以进行加锁
			strcpy(stu.stuname, "陈茹涵");
			strcpy(stu.sexy, "男生");
			pthread_mutex_unlock(&mutex);//解锁
		}
		else
		{
			pthread_mutex_lock(&mutex);
			strcpy(stu.stuname, "林夕梦");
			strcpy(stu.sexy, "女生");
			pthread_mutex_unlock(&mutex);
		}
		num++;
		//sleep(1);
	}
}

void* pthread_function2(void* pv)
{
	while (1)
	{
		pthread_mutex_lock(&mutex);
		cout  << "stu.stuname = "<<stu.stuname << "stu.sexy = " << stu.sexy << endl;
		pthread_mutex_unlock(&mutex);
		//sleep(1);
	}
}

int main()
{
	pthread_t threadid1 = 0;
	pthread_t threadid2 = 0;

	//互斥量初始化
	pthread_mutex_init(&mutex,NULL);

	pthread_create(&threadid1, NULL, pthread_function1, NULL);
	pthread_create(&threadid1, NULL, pthread_function2, NULL);

	while (1)
	{

	}
    //如果是服务器放服务器停止代码 如果是客户端放程序结束的时候
    //pthread_mutex_destroy(&mutex);

	return 0;
}

通过互斥量 解决 线程同步问题 

小结:

1.锁加在哪里:线程哪些代码在"触碰"共享数据的代码,加锁在操作共享数据之前,解锁在操作共享数据之后

2.哪些线程需要加锁:所有对共享数据的操作的线程都要加锁

错误情况:1.所有线程都必须加锁,只要有一个线程没有加锁,其他都作废

                  2.避免死锁(互斥量保证有且只有一个) 

2.3 线程信号量

用信号量进行同步

•“信号量”的作用就像是代码周围的门卫
•二进制信号量:一种最简单的一种信号量,只有“0”,“1”两种取值
•计数信号量:有更大的取值范围,一般用于希望有限个线程去执行一段给定的代码
•头文件 <semaphore.h>
•信号量函数的名字都以“sem_”打头
•信号量对象用sem_t表示

创建一个信号量

作用:对给定的信号量对象进行初始化

•sem: 要进行初始化的信号量对象
•pshared:控制着信号量的类型,如果值为0,表示它是当前进程的局部信号量;否则,其他进程就能够共享这个信号量
•value:赋给信号量对象的一个整数类型的初始值
•调用成功时 返回 0;

sem_post函数-解锁

作用:给信号量的值加上一个“1”

•sem: 初始化的信号量对象的指针作为参数,用来改变该对象的值
•调用成功时 返回 0;
•是一个“原子操作”-即同时对同一个信号量做加“1”操作的两个线程是不会冲突的。信号量的值永远会正确地加上一个“2”,因为有两个线程试图改变它

sem_wait函数-加锁

作用:从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法

 

•sem: 初始化的信号量对象的指针作为参数,用来改变该对象的值
•调用成功时 返回 0;

也是一个“原子操作”; 

sem_destroy函数

作用:用完信号量后,对该信号量进行清理 

 

•sem: 初始化的信号量对象的指针作为参数,用来改变该对象的值
•调用成功时 返回 0;
•归还自己占有的一切资源,在清理信号量的时候如果还有线程在等待它,用户就会收到一个错误
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
#include<semaphore.h>
using namespace std;

typedef struct student
{
	char stuname[20];
	char sexy[6];
}STU;

STU stu = { 0 };

//线程信号量
sem_t sem;


void* pthread_function1(void* pv)
{
	int num = 0;
	while (1)
	{
		if (num % 2 == 0)
		{
			sem_wait(&sem);//加锁
			strcpy(stu.stuname, "陈茹涵");
			strcpy(stu.sexy, "男生");
			sem_post(&sem);//解锁
		}
		else
		{
			sem_wait(&sem);
			strcpy(stu.stuname, "林夕梦");
			strcpy(stu.sexy, "女生");
			sem_post(&sem);
		}
		num++;
		//sleep(1);
	}
}

void* pthread_function2(void* pv)
{
	while (1)
	{
		sem_wait(&sem);
		cout  << "stu.stuname = "<<stu.stuname << "stu.sexy = " << stu.sexy << endl;
		sem_post(&sem);
		//sleep(1);
	}
}

int main()
{
	pthread_t threadid1 = 0;
	pthread_t threadid2 = 0;

	//线程信号量初始化
	sem_init(&sem, 0, 1);

	pthread_create(&threadid1, NULL, pthread_function1, NULL);
	pthread_create(&threadid1, NULL, pthread_function2, NULL);

	while (1)
	{

	}

    //如果是服务器放服务器停止代码 如果是客户端放程序结束的时候
    //sem_destroy(&mutex);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125884331