一、什么是信号量
线程的信号量与进程间通信中使用的信号量是一样的,它是一个特殊的变量。可以被增加和减少,但对其的关键操作访问必须保证原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都依次进行。
二、信号量的函数
信号量的函数都是以sem_开头,线程中使用的基本信号量函数有4个,在头文件semaphore.h中。
1、sem_init函数
该函数用于创建信号量,其原型如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
(1) sem为要初始化的信号量指针。
(2)pshared表示此信号在进程间共享还是在线程间共享,0表示在线程间共享,1表示在进程间共享。
(3)value为信号量的初始值。
2、sem_post函数
该函数的原型如下:
int sem_post(sem_t *sem);
(1)该函数用于以原子操作的方式将信号量加1,并且会抛出信号,原本被阻塞的sem_wait将被打开。
(2)调用成功返回0,失败返回-1,并且设置error
3、sem_wait函数
该函数的原型如下:
int sem_wait(sem_t *sem);
(1)该函数用于以原子操作的方式将信号量加-1,等待sem_post抛出信号来,否则一直阻塞。
(2)调用成功返回0,失败返回-1,并且设置error
4、sem_destory函数
该函数原型如下:
int sem_destroy(sem_t *sem);
(1)该函数对用完的信号量清理
(2)调用成功返回0,失败返回-1,并且设置error
三、写一个简单的示例
用多线程实现输入一个非“end”的字符串,并且算出字符串的长度,输入“end”时则结束程序,要求用主线程实现输入并检查字符串是否为“end”,创建的新线程用来计算字符串的长度。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
char buf[100] = {0};
sem_t sem;
int flag = 0;
void *func(void *argv)
{
while(flag==0)
{
sem_wait(&sem);
if(strcmp("end",buf))
{
printf("输入的字符串长度为%d\n",strlen(buf));
memset(buf,0,sizeof(buf));
printf("请输入一个非\"end\"的字符串\n");
}
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t th;
/*创建一个新线程计算字符串的长度*/
if(pthread_create(&th,NULL,func,NULL))
{
printf("创建新线程失败\n");
}
/*初始化线程间同步的信号量*/
if(sem_init(&sem,0,0))
{
printf("初始化用于线程间同步的信号量失败\n");
}
printf("请输入一个非\"end\"的字符串\n");
while(scanf("%s",buf))
{
if(strcmp("end",buf)==0)
{
sem_post(&sem);//唤醒正在等待的所有线程
flag = 1;
break;
}
sem_post(&sem);//唤醒正在等待的所有线程
}
/*回收子线程*/
if(pthread_join(th,NULL))
{
printf("子线程回收失败\n");
}
else
{
printf("子线程回收成功\n");
}
/*回收sem信号量*/
sem_destroy(&sem);
return 0;
}
五、运行结果
root@ubuntu:/home/yong/c_c++/2019/9.9#
root@ubuntu:/home/yong/c_c++/2019/9.9# ./a.out
请输入一个非"end"的字符串
dwed
输入的字符串长度为4
请输入一个非"end"的字符串
fef
输入的字符串长度为3
请输入一个非"end"的字符串
efewf
输入的字符串长度为5
请输入一个非"end"的字符串
fwew
输入的字符串长度为4
请输入一个非"end"的字符串
end
子线程回收成功
root@ubuntu:/home/yong/c_c++/2019/9.9#
六、什么是互斥锁 ?
互斥锁简单的可以认为是一种特殊的信号量,它的值只能是0和1,也就是上锁和解锁两种状态。
七、互斥锁函数
它们都在头文件pthread.h中,文件要加上#include<pthread.h>
1、pthread_mutex_init()
该函数的原型是:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
(1)mutex指向要初始化的互斥锁对象
(2)attr位互斥属性,用NULL表示该属性缺省
(3)成功则返回0;失败则设置error
2、pthread_mutex_lock()
该函数原型是:
int pthread_mutex_lock(ptread_mutex_t *mutex);
(1)mutex是互斥对象指针
(2)成功返回0,失败则设置error
(3)设置上锁,对于另外一个pthread_mutex_lock()函数则表示阻塞
3、pthread_mutex_unlock()
(1)mutex是互斥对象指针
(2)成功返回0,失败则设置error
(3)设置解锁,解开pthread_mutex_lock()的锁
4、pthread_mutex_destroy()
(1)mutex是互斥对象指针
(2)成功返回0,失败则设置error
(3)销毁一个互斥锁
八、用互斥锁来完成上面的需求
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
char buf[100] = {0};
int flag = 0;
pthread_mutex_t mutex;
void *func(void *argv)
{
sleep(1);//延时一秒,因为先创建了子线程,子线程的中的循环要在主线程后面,所以需要延时
while(flag==0)
{
pthread_mutex_lock(&mutex);//上锁,对一个上锁函数阻塞
if(strcmp("end",buf))
{
printf("输入的字符串长度为%d\n",strlen(buf));
memset(buf,0,sizeof(buf));
printf("请输入一个非\"end\"的字符串\n");
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main(void)
{
pthread_t th;
/*创建一个新线程计算字符串的长度*/
if(pthread_create(&th,NULL,func,NULL))
{
printf("创建新线程失败\n");
}
/*初始化线程间互斥锁*/
if(pthread_mutex_init(&mutex,NULL))
{
printf("初始化用于线程间同步的信号量失败\n");
}
printf("请输入一个非\"end\"的字符串\n");
while(1)
{
pthread_mutex_lock(&mutex);//上锁,对另一个函数进行阻塞
scanf("%s",buf);
if(strcmp("end",buf)==0)
{
flag = 1;
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);//解锁
sleep(1);//让子线程先执行
}
/*回收子线程*/
if(pthread_join(th,NULL))
{
printf("子线程回收失败\n");
}
else
{
printf("子线程回收成功\n");
}
/*销毁互斥锁*/
if(pthread_mutex_destroy(&mutex))
{
printf("销毁互斥锁失败\n");
}
return 0;
}
运行结果
root@ubuntu:/home/yong/c_c++/2019/9.9# ./a.out
请输入一个非"end"的字符串
wdew
输入的字符串长度为4
请输入一个非"end"的字符串
refregerg
输入的字符串长度为9
请输入一个非"end"的字符串
regerg
输入的字符串长度为6
请输入一个非"end"的字符串
reger
输入的字符串长度为5
请输入一个非"end"的字符串
ergerg
输入的字符串长度为6
请输入一个非"end"的字符串
end
子线程回收成功
root@ubuntu:/home/yong/c_c++/2019/9.9#