有名信号量
(1)名字:“/myname”
注意:名字必须以/开头
(2)使用有名信号量需要用到pthread库(线程库)
复制代码
如何使用这个信号量?
1、通过sem_open(),打开信号量
2、直接通过sem_wait(),sem_post()进行P、V操作
3、通过sem_close()关闭信号量
4、通过sem_unlink()删除信号量,释放资源
复制代码
1、通过sem_open(),打开信号量
2、直接通过sem_wait(),sem_post()进行P、V操作
3、通过sem_close()关闭信号量、通过sem_unlink()删除信号量,释放资源
示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>
char str[20];
#define SPACE "/space"
#define DATA "/data"
sem_t *space;
sem_t *data;
void *func1(void *arg)
{
while(1)
{
//P操作 空间资源-1
sem_wait(space);
scanf("%s", str);
//V操作 数据资源+1
sem_post(data);
}
}
void *func2(void *arg)
{
while(1)
{
//P操作 数据资源-1
sem_wait(data);
printf("%s\n", str);
//V操作 空间资源+1
sem_post(space);
}
}
void f(int sig)
{
sem_close(space);
sem_unlink(SPACE);
sem_close(data);
sem_unlink(DATA);
exit(0);
}
int main(int argc, char const *argv[])
{
//
signal(2, f);
void *p[2] = {func1, func2};
pthread_t pid[2];
for (int i = 0; i < 2; ++i)
{
pthread_create(&pid[i], NULL, p[i], NULL);
}
space = sem_open(SPACE, O_CREAT, 0666, 1);
data = sem_open(DATA, O_CREAT, 0666, 0);
while(1);
return 0;
}
复制代码
运行结果
无名信号量
这种信号量的使用步骤是:
1,在这些线程都能访问到的区域定义这种变量(比如全局变量),类型是 sem_t。
2,在任何线程使用它之前,用 sem_init( )初始化他。
3,使用 sem_wait( )/sem_trywait( )和 sem_post( )来分别进行 P、V 操作。
4,不再需要时,使用 sem_destroy( )来销毁他。
复制代码
示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>
char str[20];
sem_t space;
sem_t data;
void *func1(void *arg)
{
while(1)
{
//P操作 空间资源-1
sem_wait(&space);
scanf("%s", str);
//V操作 数据资源+1
sem_post(&data);
}
}
void *func2(void *arg)
{
while(1)
{
//P操作 数据资源-1
sem_wait(&data);
printf("%s\n", str);
//V操作 空间资源+1
sem_post(&space);
}
}
void f(int sig)
{
sem_destroy(&data);
sem_destroy(&space);
exit(0);
}
int main(int argc, char const *argv[])
{
//
signal(2, f);
sem_init(&space, 0, 1); //空间资源(写资源) 1
sem_init(&data, 0, 0); //数据资源(读资源) 0
void *p[2] = {func1, func2};
pthread_t pid[2];
for (int i = 0; i < 2; ++i)
{
pthread_create(&pid[i], NULL, p[i], NULL);
}
while(1);
return 0;
}
复制代码
互斥锁:
Pthread_mutex_t m;//定义一个互斥锁变量
//初始化
pthread_mutex_init(&m, NULL);
//上锁
pthread_mutex_lock(&m);
//解锁
pthread_mutex_unlock(&m);
//销毁锁
pthread_mutex_destroy(&m);
复制代码
使用互斥锁容易出现死锁
(1)多个线程交替上锁解锁2个锁的时候,有可能出现死锁
Pthread_mutex_lock(&m1);
Printf(“做一件2s的事情”);
Sleep(2);
Pthread_mutex_lock(&m2);
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);
Pthread_mutex_lock(&m2);
Printf(“做一件2s的事情”);
Sleep(2);
Pthread_mutex_lock(&m1);
Pthread_mutex_unlock(&m2);
Pthread_mutex_unlock(&m1);
复制代码
(2)某个线程上锁之后,在没有解锁的情况下,被异常杀死,那么需要获取同一个锁的其他线程会卡死
(3)一个任务上锁过后,未解锁之前,在上同一把锁,也会出现死锁
所以:
为线程创建取消例程,释放有可能没有释放的
尽量避免在一组锁的上锁解锁过程中,在次上同一把锁或其他锁
互斥锁保护的临界区不要太长,也不要嵌套太多函数
示例代码:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>
//定义一个互斥锁变量
pthread_mutex_t m;
pthread_mutex_t m1;
int num;
void *func1(void *arg)
{
while(1)
{
//加锁
pthread_mutex_lock(&m);
scanf("%d", &num);
//解锁
pthread_mutex_unlock(&m);
usleep(10);
}
}
void *func2(void *arg)
{
sleep(1);
while(1)
{
//加锁
pthread_mutex_lock(&m);
printf("num = %d\n", num);
//解锁
pthread_mutex_unlock(&m);
usleep(10);
}
}
int main(int argc, char const *argv[])
{
//初始化互斥锁
pthread_mutex_init(&m, NULL);
pthread_mutex_init(&m1, NULL);
//创建两个线程
pthread_t pid1, pid2;
pthread_create(&pid1, NULL, func1, NULL);
pthread_create(&pid2, NULL, func2, NULL);
while(1)
{
}
return 0;
}
复制代码
读写锁:
读写锁跟互斥锁,几乎使用一模一样,唯一的区别是在加锁的时候,可以选择加写锁还是读锁,写锁,只能加一个,读锁可以加多个
//定义读写锁变量
Pthread_rwlock_t rwlock;
//初始化读写锁
Pthread_rwlock_init(&rwlock, NULL);
//加读锁
Pthread_rwlock_rdlock(&rwlock);
//加写锁
Pthread_rwlock_wrlock(&rwlock);
//解锁
Pthread_rwlock_unlock(&rwlock);
//销毁锁
Pthread_rwlock_destroy(&rwlock);
复制代码
示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>
//定义一个读写锁变量
pthread_rwlock_t rwlock;
int num;
void dent()
{
pthread_rwlock_rdlock(&rwlock);
printf("线程1的锁状态\n");
pthread_rwlock_unlock(&rwlock);
}
void dent1()
{
pthread_rwlock_wrlock(&rwlock);
printf("线程2的锁状态\n");
pthread_rwlock_unlock(&rwlock);
}
void *func1(void *arg)
{
while(1)
{
//加写锁
pthread_rwlock_rdlock(&rwlock);
scanf("%d", &num);
//解锁
pthread_rwlock_unlock(&rwlock);
dent();
usleep(1000);
}
}
void *func2(void *arg)
{
sleep(1);
while(1)
{
//加读锁
pthread_rwlock_wrlock(&rwlock);
printf("num2 = %d\n", num);
//解锁
pthread_rwlock_unlock(&rwlock);
dent1();
usleep(1000);
}
}
int main(int argc, char const *argv[])
{
//初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
//创建两个线程
pthread_t pid1, pid2;
pthread_create(&pid1, NULL, func1, NULL);
pthread_create(&pid2, NULL, func2, NULL);
while(1)
{
}
return 0;
}
复制代码
运行结果
条件变量:
//定义条件变量的变量
Pthread_cond_t cond;
//初始化
Pthread_cond_init(&cond, 0);
//销毁条件变量
Pthread_cond_destroy(&cond);
复制代码
//进入条件变量的等待队列
Pthread_cond_wait(&cond, &m);
Struct timespec t;
Pthread_cond_timedwait(&cond, &m, &t);
复制代码
//唤醒一个
Pthread_cond_signal(&cond);
//唤醒全部
Pthread_cond_broadcast(&cond);
复制代码
示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>
//定义互斥锁变量
pthread_mutex_t m;
//定义条件变量的变量
pthread_cond_t v;
int money = 0;
int flag = 0;
void *func1(void *arg) //小楠
{
//加锁
pthread_mutex_lock(&m);
while(money < 100)
{
pthread_cond_wait(&v, &m);//进入条件变量等待队列
}
money -=100;
printf("小楠拿钱 100\n");
sleep(1);
pthread_mutex_unlock(&m);
}
void *func2(void *arg) //小明
{
//加锁
pthread_mutex_lock(&m);
while(money < 100)
{
pthread_cond_wait(&v, &m);//进入条件变量等待队列
}
money -=100;
printf("小明拿钱 100\n");
sleep(1);
pthread_mutex_unlock(&m);
}
int main(int argc, char const *argv[])
{
//初始化互斥锁
pthread_mutex_init(&m, NULL);
//初始化条件变量
pthread_cond_init(&v, 0);
pthread_t pid1, pid2;
pthread_create(&pid1, NULL ,func1, NULL);
pthread_create(&pid2, NULL ,func2, NULL);
sleep(1);
pthread_mutex_lock(&m);
sleep(3);
money += 300;
pthread_cond_broadcast(&v);//唤醒
pthread_mutex_unlock(&m);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
//销毁锁、条件变量
pthread_mutex_destroy(&m);
pthread_cond_destroy(&v);
return 0;
}
复制代码
运行结果
几种线程安全机制的比较:
互斥锁:互斥操作,使用不当容易死锁(一般考虑临界资源)
读写锁:使用和互斥锁基本一致,区别是上锁上读锁和写锁(临界资源比较多的情况)
信号量:P/V操作,只要信号量大于0就可以P
条件变量:互斥锁的一个加强,当某个条件产生的时候,才能做某件事情(不是所有情况下,线程能够通过上互斥锁访问临界资源,而是有一定要求的时候)
复制代码
线程池:
1,任务队列中刚开始没有任何任务,是一个具有头结点的空链队列。
2,使用互斥锁来保护这个队列。
3,使用条件变量来代表任务队列中的任务个数的变化——将来如果主线程往队列中投放任务,那么可以通过条件变量来唤醒那些睡着了的线程。
4,通过一个公共开关——shutdown,来控制线程退出,进而销毁整个线程池。
复制代码
API接口
线程池相关的结构体
表示一个任务
线程池
1、初始化线程池
2.添加任务
3、添加活跃线程
4、删除活跃线程
5、销毁线程池
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END