我们简单写个多个线程并发对一个全局变量进行++操作
(一)问题描述
创建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中的信号量接口详解
- 创建信号量
int sem_init(sem_t* sem, int pshared, unsigned int value);
- sem : 初始化信号量对象
- pshared : 控制信号量的类型,0为当前进程的局部信号量,否则就可以在多个进程之间共享
- value :
-
信号量原子-1操作
int sem_wait(sem_t* sem);
(非阻塞版本sem_trywait) -
信号量原子+1操作
int sem_post(sem_t* sem);
-
清理信号量
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;
}
- 结果: