Contents
  1. 1. 函数级
    1. 1.1. 实例: 局部锁
  2. 2. 线程级
    1. 2.1. 实例:Qt中JniEnv实现
  3. 3. 结论

我们常常在代码通过类的生命周期来管理资源释放,构造函数的时候申请资源,析构函数的时候释放资源,这样可以减少程序员自动释放资源的工作,避免内存泄露。而有些场景,一个资源是线程级别共享的,线程结束后才释放,对于这种场景,可以利用Thread Local Storage机制,因为在C++中,线程局部变量的析构函数会随着线程的结束而调用

函数级

先复习一下函数级资源释。对于C++,程序员可以申请栈上面的资源,随着函数结束,资源被释放,对应的析构函数马上被调用,这个生命周期机制,能让程序员在构造和析构函数里做一些资源管理的事情

实例: 局部锁

函数级资源释放应用最多的就是局部锁,在构造函数获取锁,在析构的时候释放锁

template <class Lockable>
class LockScope
{
public:
LockScope(Lockable & m) : mtx(m)
{
mtx.lock();
}
~LockScope()
{
mtx.unlock();
}
private:
Lockable & mtx;
};

调用代码如下:

void func()
{
LockScope(lock);
//...
}

线程级

相对于函数级别的资源释放,线程级别通常是一个线程局部对象,随着线程的结束,该对象会自动调用析构函数,跟函数级思路一致

实例:Qt中JniEnv实现

在Jni中,获取JNIEnv时,在使用前要调用AttachCurrentThread,使用后要调用DetachCurrentThread,否则线程无法正常退出,这就给程序员比较大压力,我们需要想一种方法来封装这个操作,Qt中的全局JniEnv实现给了我们一种思路

如下面代码所示,有一个全局静态变量QJNIEnvironmentPrivateTLS,存放在QThreadStorage中,表示是线程局部变量,它的析构函数是执行DetachCurrentThread操作,表示在线程退出时会调用这个操作。其中QThreadStorage的实现类似Java的ThreadLocal,是实现线程局部变量的关键

class QJNIEnvironmentPrivateTLS
{
public:
inline ~QJNIEnvironmentPrivateTLS()
{
QtAndroidPrivate::javaVM()->DetachCurrentThread();
}
};

Q_GLOBAL_STATIC(QThreadStorage<QJNIEnvironmentPrivateTLS*>, jniEnvTLS)

QJNIEnvironmentPrivate在构造函数中会获取JNIEnv,如果发现没有调用过AttachCurrentThread则马上Attach一下,

每个线程在AttachCurrentThread时会在线程局部变量中初始化一个QJNIEnvironmentPrivateTLS实例,这样可以保证在线程退出时会调用相应的DetachCurrentThread操作

static const char qJniThreadName[] = "QtThread";

QJNIEnvironmentPrivate::QJNIEnvironmentPrivate()
: jniEnv(0)
{
JavaVM* vm = QtAndroidPrivate::javaVM();
const jint ret = vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
if (ret == JNI_OK) // Already attached
return;

if (ret == JNI_EDETACHED) { // We need to (re-)attach
JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, Q_NULLPTR };
if (vm->AttachCurrentThread(&jniEnv, &args) != JNI_OK)
return;

if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
jniEnvTLS->setLocalData(new QJNIEnvironmentPrivateTLS);
}
}

QJNIEnvironmentPrivate还重载了->方法,这样使用者就可以通过QJNIEnvironmentPrivate来使用JniEnv,而不用考虑AttachCurrentThread和DetachCurrentThread操作

JNIEnv *QJNIEnvironmentPrivate::operator->()
{
return jniEnv;
}

调用代码如下:

//other Jni module
void jniTest(long value)
{
QJNIEnvironmentPrivate env;
jlong j_value = (jlong) value;
jmethodID jmethod =env->GetMethodID(jclass_callback_global_, "onTest", "(J)V");
env->CallVoidMethod(jobject_callback_global_, jmethod,j_value);
}

如果线程只执行这个jniTest函数,随着函数执行,线程结束,QtAndroidPrivate::javaVM()->DetachCurrentThread()会被自动调用

结论

如果不采用线程级资源管理的方式,就会在代码中布满了一对对AttachCurrentThread方法DetachCurrentThread,十分容易出Bug,对JNIEnv的封装也让其他Jni模块可以方便的使用Jni功能

Contents
  1. 1. 函数级
    1. 1.1. 实例: 局部锁
  2. 2. 线程级
    1. 2.1. 实例:Qt中JniEnv实现
  3. 3. 结论