【Linux】线程同步(信号量)


我们简单写个多个线程并发对一个全局变量进行++操作


(一)问题描述

创建5个线程,一个全局变量gdata = 0; 让每个线程都对gdata进行++100次,理想结果就是499

(1)未同步(异步)多线程代码

  • 代码
#include <stdio.h>
#include <string.h>
#include <pthread.h>

int gdata = 0;

void* pthread_fun(void* arg)
{
    
    
	for(int i = 0; i < 100; i++)
	{
    
    
		printf("gdata_after = %d\n", gdata++);
	}
}

int main()
{
    
    
	pthread_t id[5];
	//创建5个线程
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_create(&id[i], NULL, pthread_fun, NULL);
	}

	//等待五个进程结束
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_join(id[i], NULL);
	}
	return 0;
}
  • 结果:可以看到gdata时而499,时而498,很明显程序有问题!!
    在这里插入图片描述

(二)信号量控制线程同步

思路:使用一个信号量加以控制,即可,因为不在乎是那个线程先运行,所以只要保证再gdata++的时候,让其他的线程阻塞就好了,这样就能保证最后的结果== 499

(1)自定义信号量实现线程同步

代码:

  • sem.h
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>

union semun
{
    
    
	int val;
};

void sem_init();

void sem_p();

void sem_v();

void sem_destroy();
  • sem.c
#include "sem.h"

//信号量集的id
static int semid = -1;

void sem_init()
{
    
    
	//创建
	semid = semget((key_t)8888, 1, IPC_CREAT | IPC_EXCL | 0600);
	if(semid == -1)
	{
    
    
		semid = semget((key_t)8888, 0, IPC_CREAT);
		if(semid == -1)
		{
    
    
			perror("semget err");
			return;
		}
	}
	//初始化
	else
	{
    
    
		//定义1个信号量
		union semun sem_val;
		sem_val.val = 1;
		
		if(semctl(semid, 0, SETVAL, sem_val) == -1)
		{
    
    
			perror("sem_val init err");
			return;
		}

	}
}

void sem_p()
{
    
    
	struct sembuf buf;
	buf.sem_num = 0;
	buf.sem_op = -1;
	buf.sem_flg = SEM_UNDO;

	if(semop(semid, &buf, 1) == -1)
	{
    
    
		printf("sem_p err\n");
		return;
	}

}

void sem_v(int index)
{
    
    
	struct sembuf buf;
	buf.sem_num = 0;
	buf.sem_op = 1;
	buf.sem_flg = SEM_UNDO;

	if(semop(semid, &buf, 1) == -1)
	{
    
    
		printf("sem_v err\n");
		return;
	}
}

void sem_destroy()
{
    
    
	if(semctl(semid, 0, IPC_RMID) == -1)
	{
    
    
		perror("sem_destory err");
		return;
	}
}
  • new.c
#include "sem.h"
#include <pthread.h>
#include <unistd.h>
int gdata = 0;

void* pthread_fun(void* arg)
{
    
    
	int* index = (int*)arg;
	
	sem_p(*index);

	for(int i = 0; i < 100; i++)
	{
    
    
		printf("gdata_after = %d\n", gdata++);
	}
	sem_v(*index);

}

int main()
{
    
    
	//初始化信号量集
	sem_init();

	pthread_t id[5];
	//创建5个线程
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_create(&id[i], NULL, pthread_fun, &i);
	}

	//等待五个进程结束
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_join(id[i], NULL);
	}

	//销毁信号量集
	sem_destroy();
	return 0;
}
  • 结果:在多次实验下,使用信号量保证了线程安全
    在这里插入图片描述

(2)semaphore.h中的信号量接口详解

  1. 创建信号量
    int sem_init(sem_t* sem, int pshared, unsigned int value);
  • sem : 初始化信号量对象
  • pshared : 控制信号量的类型,0为当前进程的局部信号量,否则就可以在多个进程之间共享
  • value :
  1. 信号量原子-1操作
    int sem_wait(sem_t* sem); (非阻塞版本sem_trywait)

  2. 信号量原子+1操作
    int sem_post(sem_t* sem);

  3. 清理信号量
    int sem_destroy(sem_t* sem);

  • 返回值:成功0

(3)使用系统提供的信号量接口

  • nnew.c
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

int gdata = 0;
sem_t sem;
void* pthread_fun(void* arg)
{
    
    
	sem_wait(&sem);
	for(int i = 0; i < 100; i++)
	{
    
    
		printf("gdata_after = %d\n", gdata++);
	}
	sem_post(&sem);
}

int main()
{
    
    
	//初始化信号量集
	sem_init(&sem, 0, 1);
	pthread_t id[5];
	//创建5个线程
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_create(&id[i], NULL, pthread_fun, NULL);
	}
	//等待五个进程结束
	for(int i = 0; i < 5; i++)
	{
    
    
		pthread_join(id[i], NULL);
	}
	//销毁信号量集
	sem_destroy(&sem);
	return 0;
}
  • 结果:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaoxiaoguailou/article/details/121580048