1.进程互斥锁
1.1 与线程锁类型相同,也是 pthread_mutex_t 类型
1.2 修改线程锁属性,将其更改为进程锁
1.3 原子锁相关函数
- pthread_mutexattr_t : 原子锁属性类型
- pthread_mutexattr_inti(pthread_mutexattr_t*) : 初始化原子锁属性
- pthread_mutexattr_destroy(pthread_mutexattr_t*) : 销毁原子锁属性
- pthread_mutexattr_setpshared(pthread_mutexattr_t*, int pshared) : 更改原子锁属性
1.4 通过修改线程锁属性,使用进程锁完成父子进程对共享资源操作
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define COUNT 5000
typedef struct
{
int n_code;
pthread_mutex_t process_lock;
}ProData;
int main()
{
int fd;
pthread_mutexattr_t attr;
ProData* p_pro;
fd = open("ProcessFile", O_RDWR|O_CREAT, 0664);
ftruncate(fd, sizeof(ProData));
p_pro = mmap(NULL, sizeof(ProData), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
bzero(p_pro, sizeof(ProData));
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&(p_pro->process_lock), &attr);
pthread_mutexattr_destroy(&attr);
pid_t pid;
pid = fork();
if(pid > 0)
{
for(int i=0; i<COUNT; i++)
{
pthread_mutex_lock(&(p_pro->process_lock));
printf("Parent PID:%d\t++code:%d\n", getpid(), ++(p_pro->n_code));
pthread_mutex_unlock(&(p_pro->process_lock));
}
}
else if(pid == 0)
{
for(int i=0; i<COUNT; i++)
{
pthread_mutex_lock(&(p_pro->process_lock));
printf("Child PID:%d\t++code:%d\n", getpid(), ++(p_pro->n_code));
pthread_mutex_unlock(&(p_pro->process_lock));
}
exit(0);
}
else
{
perror("fork error");
exit(0);
}
pid_t wpid;
wpid = wait(NULL);
printf("Wait PID:%d\n", wpid);
return 0;
}
2.文件锁
2.1 Linux中使用文件锁时一般可采用 lockf 或 fcntl 函数
2.2 lockf 为文件建议锁, fcntl 为可选择建议锁或强制锁
2.3 例子
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
void sys_err(char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int fd;
struct flock f_lock;
if (argc < 2) {
printf("./ts filename\n");
exit(1);
}
if ((fd = open(argv[1], O_RDWR)) < 0)
sys_err("open");
f_lock.l_type = F_RDLCK;
f_lock.l_whence = SEEK_SET;
f_lock.l_start = 0;
f_lock.l_len = 0;
fcntl(fd, F_SETLKW, &f_lock);
printf("get flock\n");
sleep(10);
f_lock.l_type = F_UNLCK;
fcntl(fd, F_SETLKW, &f_lock);
printf("un flock\n");
close(fd);
return 0;
}
3.信号量
3.1 信号量分为一元灯、多元灯
3.2 一元灯创建过程
- 1.定义信号量类型
- 2.初始化信号量(设置灯值,灯值为1即为一元信号灯),信号量创建出的临界区通过灯值访问,只有灯值大于0时允许进入临界区,进入后灯值减1,使用临界区代码,使用完后将灯值加1;
3.3 使用信号量的一元灯与互斥锁没有区别(都是锁资源的)
3.4 多元灯(灯值大于1):用来锁流程业务
3.5 信号量函数
4.死锁
4.1 概念
- 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。
- 简述:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
4.2 使用线程同步技术要尽力避免死锁问题
4.3 死锁问题就是指多线程访问数据混乱,例如两个线程相互申请他人已占用的资源,导致操作阻塞
4.4 死锁的必要条件:
- 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
- 请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。
- 非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。
- 循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
4.5 解决死锁的方式:
- 1.服务者模式:每次使用全局变量之前都进行询问,可以使用则使用,否则继续等待;
- 2.银行家模式:判断资源使用的风险,风险较大则拒绝使用,否则可以使用。
5.无锁编程与有锁编程的区别
- 速度最快的是什么都没有的编程(基础编程);其次是无锁编程;最后是有锁编程(加锁解锁会消耗大量的时间资源);
- 无锁编程无法再次进行优化,有锁编程可以进行再优化(减少锁的使用频率);
- 无锁编程简单,易于实现,但是无法再优化。