线程互斥机制--信号量的使用

目录

前言

一、信号量是什么?

1.量即数量,计数器

2.工作原理

二、信号量的使用

1.引入头文件

2.声明信号量

3.初始化信号量

4.sem_post()释放信号量

5.sem_wait()申请信号量

6.sem_getvalue()获得当前信号量的值

7.sem_destroy()摧毁信号量

三、代码示例

1.fgets()函数:

2.strcmp()函数:


前言

在多线程编程中使用广泛的一种机制——信号量


一、信号量是什么?

1.量即数量,计数器

信号量本身代表一种资源,其本质是一个非负的整数计数器,被用来控制对公共资源的访问。换句话说,信号量的核心内容是信号量的值

2.工作原理

1.所有对共享资源操作的线程,在访问共享资源之前,都需要先操作信号量的值。操作信号量的值又可以称为PV操作,P操作为申请信号量,V操作为释放信号量

2.当申请信号量成功时,信号量的值减1,而释放信号量成功时,信号量的值加1。但是当信号量的值为0时,申请信号量时将会阻塞,其值不能减为负数

二、信号量的使用

1.引入头文件

#include <semaphore.h>

注:信号量作为一种同步互斥机制,若用于实现互斥时,多线程只需设置一个信号量。若用于实现同步时,则需要设置多个信号量,并通过设置不同的信号量的初始值来实现线程的执行顺序

2.声明信号量

类型sem_t

例:sem_t sem;

3.初始化信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);

参数:

参数sem表示信号量的标识符

pshared参数用来设置信号量的使用环境,其值为0,表示信号量用于同一个进程的多个线程之间使用;其值为非0,表示信号量用于进程间使用

value为重要的参数,表示信号量的初始值

成功返回0,失败返回-1

4.sem_post()释放信号量

当释放信号量成功时,信号量的值加1

int sem_post(sem_t *sem);

5.sem_wait()申请信号量

int sem_wait(sem_t *sem)

当申请信号量成功时,信号量的值减1,当信号量的值为0时,此操作将会阻塞,直到其他线程执行释放信号量

sem_trywait()函数:

int sem_trywait(sem_t *sem);

与sem_wait()函数类似,唯一的区别在于sem_trywait()函数不会阻塞,当信号量为0时,函数直接返回错误码EAGAIN

sem_timewait()函数:

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

多了参数abs_timeout,用来设置时间限制,如果在该时间内,信号量仍然不能申请,那么该函数不会一直阻塞,而是返回错误码ETIMEOUT

6.sem_getvalue()获得当前信号量的值

int sem_getvalue(sem_t *sem, int *sval);

sem_getvalue()函数用于获得当前信号量的值,并将值保存在参数sval中

7.sem_destroy()摧毁信号量

int sem_destroy(sem_t *sem);

三、代码示例

此程序实现从键盘输入字符串,并把小写字符转成大写,两线程工作,当键盘输入end,退出程序

#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
    
//线程函数
void *thread_func(void *msg);
sem_t sem;//信号量
    
#define MSG_SIZE 512
    
int main()
{
     int res = -1;
     pthread_t thread;
     void *thread_result = NULL;
     char msg[MSG_SIZE];
     //初始化信号量,其初值为0
     res = sem_init(&sem, 0, 0);
     if(res == -1)
     {
     perror("semaphore intitialization failed\n");
     exit(EXIT_FAILURE);
     }
     //创建线程,并把msg作为线程函数的参数
     res = pthread_create(&thread, NULL, thread_func, msg);
     if(res != 0)
     {
     perror("pthread_create failed\n");
     exit(EXIT_FAILURE);
     }
     //输入信息,以输入end结束,由于fgets会把回车(\n)也读入,所以判断时就变成了“end\n”
     printf("Input some text. Enter 'end'to finish...\n");
     while(strcmp("end\n", msg) != 0)//strcmp()相同返回0,输入字符串后外加一个enter键,相当于一个\n
     {
     fgets(msg, MSG_SIZE, stdin);//将键盘输入的字符送入meg中
     //把信号量加1
     sem_post(&sem);//发信号
     }
     printf("Waiting for thread to finish...\n");
     //等待子线程结束
     res = pthread_join(thread, &thread_result);
     if(res != 0)
     {
     perror("pthread_join failed\n");
     exit(EXIT_FAILURE);
     }
     printf("Thread joined\n");
     //清理信号量
     sem_destroy(&sem);
     exit(EXIT_SUCCESS);
}
    
void* thread_func(void *msg)
{
     //把信号量减1
     sem_wait(&sem);
     char *ptr = msg;
     while(strcmp("end\n", msg) != 0)
     {
     int i = 0;
     //把小写字母变成大写
     for(; ptr[i] != '\0'; ++i)
     {
         if(ptr[i] >= 'a' && ptr[i] <= 'z')
         {
         ptr[i] -= 'a' - 'A';
         //ptr[i]=ptr[i]-'a'+'A';
         }
     }
     printf("You input %d characters\n", i-1);
     printf("To Uppercase: %s\n", ptr);
     //把信号量减1
     sem_wait(&sem);
     }
     //退出线程
     pthread_exit(NULL);
}

此处介绍两函数:

1.fgets()函数:

原型:

char *fgets(char *str, int n, FILE *stream)

从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内

参数:

1.str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串

2.n -- 这是要读取的最大字符数

3.stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流

返回值:

成功:返回相同的 str 参数

失败:空指针

2.strcmp()函数:

原型:

int strcmp(const char *str1, const char *str2)

把 str1 所指向的字符串和 str2 所指向的字符串进行比较

参数

str1 -- 要进行比较的第一个字符串。

str2 -- 要进行比较的第二个字符串。

返回值:

若str1=str2,则返回零;

若str1<str2,则返回负数;

若str1>str2,则返回正数。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香菜是个好东西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值