NDK 线程同步
使用场景
对底层代码进行 HOOK, 不可避免的要考虑多线程同步问题, 当然也可以写个类似 java 的线程本地变量来隔离内存空间。
死锁分析
恩, 道理其实大家都懂的, 毕竟大学就学了操作系统,理论神马的窝就不讲了哈, 这里说说我的处理方法。首先线程同步问题主要是多线程对相同的可写内存进行操作导致的, 辣么我们给这些可写的内存,每个内存都加把锁不就得了, 哈哈,就这么简单, 我们将存在多个线程访问的一块内存(比如说一个变量, 一个结构体,一个类)都配把锁, 这样确实就解决线程同步问题了, 但是作为一只程序员,必须打起12分的精神,时刻小心自己是否会掉坑里面去,这里也是哦, 如果按上面这么做,确实解决了多线程数据不一致问题,但是极有可能导致其他问题的出现, 比如死锁, 性能低下等, 当然咱也不需要慌, 上面的设计思路是正确的(可写的多线程共享内存加锁),我们可以添加一些规范来规避这些问题;要想解决一个问题, 我们必须能够知道这个问题发生的原因是什么
死锁的简介:
线程A1, A2, 资源 R1, R2, 对应锁 RL1, RL2
A1的某个操作需要RL1, RL2
A2的某个操作需要RL2, RL1
说明: A1在获取RL2锁的时候阻塞了, A2在获取RL1锁的时候阻塞了
RL1 RL2
A 1--------------R1-------x------R2
RL2 RL1
A2 --------------R2-------x-------R1
嘛, 重上面的分析知道,产生死锁的条件有这些:(窝的看法, 理论的说法请自行百度)
存在两个以上的锁(这是理所当然的啦, 如果只有一个锁,肿么可能存在死锁问题QAQ, 所以如果多线程共用一个锁,那就大胆的使用, 不用担心死锁啦)
线程获取锁的顺序不一致, 理论一点的说法就是资源存在环形链(可以这么说,如果有多个锁, 但是所有的获得锁的顺序是一样的,那么这些锁其实等价于一个锁, 多块内存也可以看做一个大内存啦, 这个可以用反证法证明)
如何解决死锁
我的处理方法是这样的(各位大佬有更好的想法求评论区@)
减少锁嵌套, 也就是临界区的代码量尽量小, 尽量代码分开加锁,比如一个方法可能头部几行代码需要加锁, 和结束的几行需要加同一个锁, 我们就不要将整个方法加锁了, 而是开头加锁,在结尾也加锁, 因为其他代码也有可能加锁了,如果方法整个加锁,辣么锁嵌套就没办法避免了
不要有循环锁的存在, 比如保持所有锁的获取顺序是相同的,辣么就不会有环路等待的条件啦(我在jni代码里面就是这么处理的)
linux 互斥锁
嘛,互斥锁的主要作用是为了让代码进入临界区, 让一段代码可以成为原子操作, 理论上将这个功能只能由操作系统提供, 毕竟操作系统负责任务调度, 管理中断信息, 在 Java 和c++11 中都直接提供了api,c++11 中是 std::mutex, 然而我的 ndk 环境并没有这个 api,所以就用了 linux 系统里面的 api 来实现了。
互斥锁
(互斥锁 = synchronized)
API说明
//数据结构:
pthread_mutex_t
// 行为:
// 初始化:
// 静态初始化:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 动态初始化:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
// 获得锁:
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 释放锁:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 销毁锁:
int pthread_mutex_destroy(pthread_mutex *mutex);
// 以上可以发现命名规律, 文件名前缀_抽象的工能名_具体事务
// 嘛,写c程序的话可以这么命名, 用c++的就不必啦
使用说明
初始化----加锁--------释放锁------销毁锁
下面是我在ndk中封装的Lock
//SimpleLock.h;
class SimpleLock
{
public :
SimpleLock();
void lock();
void unlock();
~SimpleLockl();
private:
pthread_mutex_t __m;
}
//SimpleLock.cpp
SimpleLock::SimpleLock()
{
__m = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
}
void SimpleLock::lock()
{
int ret = pthread_mutex_lock(&__m);
assert(ret == 0);
}
void SimpleLock::unlock()
{
int ret = pthread_mutex_unlock(&__m);
assert(ret == 0);
}
SimpleLock::~SimpleLock()
{
pthread_mutex_destory(&__m);
}
特别注意
这里的加锁和解锁应该配对,应该配对,应该配对(重要的事情要说三变) ,最后不用了应该销毁, 应为这部分内存是没办法回收的, 所以说不用的时候要销毁掉,嘛,这里就写到析构函数里面啦, 如果是 c 写的...(额, 别忘了就好)
bug: 探针的获取ndk数据代码里面也是加了锁的,然而正常情况下没啥问题,如果数据有错, 该方法会直接返回, 但是返回的时候没有解锁!!!最后第二次获取数据的时候程序无响应了!!!
条件锁
(本来只想将总结互斥锁,但是条件锁很好玩,也就顺带看一下啦)
//嘛, 条件锁的作用我理解的就是linux给我们实现的一个简单的观察者。
// 主要结构:
// 数据结构:
pthread_cond_t cond_lock;
// 初始化:
// 静态:
PTHREAD_COND_INITIALIZER
// 动态:
int pthread_cond_init(pthread_cond_t *cv,const pthread_condattr_t *cattr)
// 行为:
// 等待:
int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);
// 通知:
int pthread_cond_signal(pthread_cond_t *cv);
使用场景
在临界区中某个因为满足某个条件而阻塞, 又应为某个条件满足而被唤醒,主要是如果不用条件锁, 如果某个线程需要特定条件才能执行, 而这个条件依赖去其他线程, 那么就要让这个线程隔一段时间查询一下状态,满足则执 行,否则阻塞,这样会导致性能问题, 所以条件锁可以在状态满足条件的时候通知阻塞的线程执行。而且一般来说条件锁和互斥锁要一起用。
pthread_mutex_t m;
pthread_cond_t c;
线程一:
pthread_mutex_lock(&m);
if (条件不满足)
{
pthread_cond_wait(&c, &m);//等待状态满足后再执行
}
pthread_mutex_unlock(&m);
线程二:
pthread_mutex_lock(&m);
...
if (条件满足) //线程一可以执行了
{
pthread_cond_signal(&c);
}
...
pthread_mutex_unlock(&m);
NDK 线程同步的更多相关文章
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- C#多线程之线程同步篇3
在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...
- C#多线程之线程同步篇2
在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...
- C#多线程之线程同步篇1
在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...
- C# 线程同步的三类情景
C# 已经提供了我们几种非常好用的类库如 BackgroundWorker.Thread.Task等,借助它们,我们就能够分分钟编写出一个多线程的应用程序. 比如这样一个需求:有一个 Winform ...
- Java进击C#——语法之线程同步
上一章我们讲到关于C#线程方向的应用.但是笔者并没有讲到多线程中的另一个知识点--同步.多线程的应用开发都有可能发生脏数据.同步的功能或多或少都会用到.本章就要来讲一下关于线程同步的问题.根据笔者这几 ...
- Java多线程 3 线程同步
在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...
- JAVA之线程同步的三种方法
最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...
- 三、线程同步之Sysnchronized关键字
线程同步 问题引入 观察一面一段小程序: public class Main { private static int amount = 0; public static void main(Stri ...
随机推荐
- C#语言基础——定义变量、变量赋值、输入输出
第一部分 了解c# 一.C#的定义及其特点 C#是微软公司在2000年7月发布的一种全新且简单.安全.面向对象的程序设计语言,是专门为.net的应用而开发的语言.它吸收了c++.Visual basi ...
- Window下JDK安装与配置
今天项目组开会,由于.Net平台的限制无法满足现有业务需求,项目计划从.Net平台转Java平台,采用Java+Spark+Hadoop,之前关于Java和Hadoop的书也买的有只是平时看的少,最近 ...
- Java基础——变量、数据类型
一 .变量 1.计算机的内存类似于人的大脑,计算机使用内存来记忆大量运算时要使用数据.内存是一个物理设备,如何来存储一个数据呢?很简单,把内存想象成一间旅馆,要存储的数据就好比要住宿的客人. 首先,旅 ...
- Ubuntu 不支持 rpm
不是第一次犯这个错误了. 记一下. 每次安装jdk 的时候,习惯性下载rpm包. 然后的,然后在 ubuntu上rpm 安装的时候就跪了.. ubuntu需要使用Alien 把rpm 转成 deb再安 ...
- ASP.NET脚本过滤-防止跨站脚本攻击(收集别人的)
ASP.Net 1.1后引入了对提交表单自动检查是否存在XSS(跨站脚本攻击)的能力.当用户试图用<xxxx>之类的输入影响页面返回结果的时候,ASP.Net的引擎会引发一个 HttpRe ...
- BFS-基础简单的算法
前言 有时候,当你并不了解很多高级算法的时候,搜索不失为一种解决问题的好方法,而且很多高级算法有或多或少的会用到搜索或者搜索的思想.可见,搜索是一个基础并且必须要掌握的算法. 在这篇文章中,会对BFS ...
- ###Intent的使用(活动中穿梭)
让活动切换有两种方式 显示意图和隐式意图 显示意图:只能在本应用中穿梭: 隐式意图:可以调用其他应用程序的活动,包括系统应用,但是需要配置清单文件 显式Intent 1) 创建一个新的活动 2) 确定 ...
- Linux学习之Vim使用
一 为何要学Vim 所有的Unix Like系统都有自带vi编辑器 一些软件的编辑接口会自动调起vi 作为vi的升级版,vim具有程序编辑功能,而且具有代码颜色高亮显示.辨别代码的正确性等功能 以上优 ...
- 用Qemu搭建aarch32学习环境
作者信息 作者: 彭东林 邮箱: pengdonglin137@163.com QQ: 405728433 软件平台 主机: Ubuntu14.04 64位版本 模拟器:Qemu-2.8.0 Linu ...
- Java线程池使用和分析(一)
线程池是可以控制线程创建.释放,并通过某种策略尝试复用线程去执行任务的一种管理框架,从而实现线程资源与任务之间的一种平衡. 以下分析基于 JDK1.7 以下是本文的目录大纲: 一.线程池架构 二.Th ...