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

  • Post author:
  • Post category:其他





目录


前言


一、信号量是什么?


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,则返回正数。



版权声明:本文为m0_65731372原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。