MMKV:线程和进程处理
本文链接:MMKV:线程和进程处理_猎羽的博客-CSDN博客
POSIX是什么?
- 可移植性操作系统接口,包括了系统API
- 定义了标准的基于UNIX操作系统的系统接口和愿景
- 定义了创建和操作线程的接口
POSIX线程就是POSIX标准中的线程
- 有C++的线程,但是一般都使用POSIX线程 pthread.h
Java中有多线程去操作MMKV,涉及到底层集合,要怎么办?
- 加锁
线程操作
1、线程创建
- pthread_create(&pid, 0, run, &i)
- 参数一:线程ID pthread_t pid
- 参数二:线程属性
- 参数三:线程方法 void* run(void *args)
- 参数四:run方法的参数 int i = 100 => 多个参数怎么办?传入结构体或者对象
2、pthread_join
- pthread_join(pid, 0) // 等待线程pid执行
3、pthread_exit 强制停止,不推荐
线程同步
互斥量
4、使用互斥量
queue q; //队列 // 多线程中操作queue会出现问题!
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, 0); // 初始化 pthread_mutext_lock(&mutex); pthread_mutext_unlock(&mutex); pthread_mutext_destory(&mutex); // 销毁
- 非递归锁,不能反复上锁 => 死锁
5、如何解决可重入锁的需求?
- pthread_mutextattr_t 可以设置mutex属性
pthread_mutextattr_t attr; pthread_mutextattr_init(&attr); // 属性初始化 pthread_mutexattr_settype(&attr, PTHREAD_MUTEXT_RECURSIVE); // 设置为递归锁/可重入锁 pthread_mutext_init(&mutext, &attr); pthread_mutextattr_destory(&attr); // 属性销毁
mmkv封装
4、c++封装出ThreadLock类 => 使用等效于JUC Lock方法
- lock
- unlock
- 构造方法初始化mutex和属性
- 析构销毁
5、如何避免忘记使用unlock?
- 再次封装ThreadLock类
class Lock{ThredLock *lock; public: Lock(ThreadLock lock){ this->lock = lock; lock.lock(); } ~Lock(){ lock->unlock(); // 解锁 } }
使用
void test(){ ThreadLock *tl = new ThreadLock(); Lock Lock(tl);// 退出后会自动析构 }
- mmkv中就是ScopedLock
6、template模板类是干什么的?
- 相当于Java的泛型
7、mmkv中进一步封装了SCOPEDLOCK()
- 宏函数:全大写
#define SCOPEDLOCK(lock) _SCOPEDLOCK(lock, __COUNTER__) xxx ScopedLock<declttype(g_instanceLock)> __scopedLock0(&g_instance);
- __COUNTER__是编译器定义的宏,每次调用+1,表示宏函数调用了几次
- declttype取类型,用于泛型
- 为什么要这么封装宏函数? =>
8、哪里需要加锁?
- 构造mmkv处,集合中获取MMKV对象
- m_dic相关的getXXX()/putXXX()
多进程
1、mmkv可以保障主进程、子进程操作的数据,对方都可以访问到
文件锁
2、文件锁(flock)是什么?
- 进程a 进程b 操作同一个文件,需要保证数据的完整和可见性
- flock文件锁保证文件的同步
#include <sys/file.h> int flock(int fd, int operation); operation: 锁的类型 LOCK_SH 共享锁(读锁) LOCK_EX 排他锁(写锁) LOCK_UN 释放锁 LOCK_BN 非阻塞请求,默认都是阻塞的,可以:LOCK_SH|LOCK_BN,类似于JUC Lock的tryLock()
3、mmkv中设计文件锁需要符合哪些要求?
- 递归锁
- 文件锁是状态锁,没有计数器,加n次锁,1次解除就全部解除
- 锁升级/锁降级
- 读锁(共享锁)升级为写锁(互斥锁)
- 锁降级
- 问题:flock支持锁升级,但进程A,进程B都持有读锁,都要升级为写锁,会陷入相互等待的问题 => 产生死锁
- 文件锁不支持递归锁 == 锁会直接消失,导致不支持锁降级
4、如何设计mmkv中文件锁?如何封装?
- 增加读锁,写锁计数器
class FileLock{ size_t sharedLockCount; size_t exclusiveLockCount; bool doLock(LockType lockType, // 锁类型 bool wait); // 是否阻塞 } bool FileLock::doLock(LockType lockType, bool wait){ ->读锁 ->sharedLockCount++ ->sharedLockCount>1 //已经上过读锁,实现可重入,不用再上锁 return即可 ->exclusiveLockCount>0 //上过写锁并且未释放,不能再上读锁 return即可 ->写锁 ->exclusiveLockCount++ ->exclusiveLockCount>1 // 有其他写锁,还未释放 return ->sharedLockCount>0 // 已经当前进程持有读锁,后续尝试去加写锁(try 非阻塞方法)。可能失败:有其他进程持有了读锁,自身释放读锁,再加写锁。避免死锁 ->通过条件检查 ->flock -> }
- 进程A上读锁,再上写锁,OK(支持锁升级)
- 进程A上读锁,进程B上了读锁,进程A再上写锁,不可以!需要进程A释放读锁。再上写锁。
5、进程A和进程B都有读锁,进程A直接上写锁,为什么会导致死锁?
- 进程A想上写锁(阻塞方式),会等待进程B释放读锁
- 进程B这时候也想上写锁(阻塞方式),会等待进程A释放读锁
- 死锁
收获
1、如何保证写入日志不会因为crash导致日志不全?
- mmap,操作系统会保证
x