安卓C下,创建线程一般通过继承Thread类,并重下threadLoop()函数实现,示例代码如下:
class PoolThread : public Thread
{
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain)
{
}
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};
sp t = new PoolThread(isMain);
t->run(name.string());
实际run函数调用流:
run()
==> androidCreateRawThreadEtc(_threadLoop,this, name, priority, stack, &mThread);
==>pthread_create(&thread, &attr,
(android_pthread_entry)entryFunction, userData);
==>_threadLoop(void* user)
==>readyToRun()
==>threadLoop()
2、特别的canCallJava变量
Thread构造函数如下:
Thread::Thread(bool canCallJava)
: mCanCallJava(canCallJava),
mThread(thread_id_t(-1)),
mLock("Thread::mLock"),
mStatus(NO_ERROR),
mExitPending(false), mRunning(false)
#if defined(__ANDROID__)
, mTid(-1)
#endif
{
}
其中有一个特别的变量canCallJava,有什么作用?
canCallJava在run函数中使用,实际上线程是在run中创建的,而且canCallJava变量会使创建线程走不同的流程:
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
LOG_ALWAYS_FATAL_IF(name == nullptr, "thread name not provided to Thread::run");
Mutex::Autolock _l(mLock);
if (mRunning) {
// thread already started
return INVALID_OPERATION;
}
// reset status and exitPending to their default value, so we can
// try again after an error happened (either below, or in readyToRun())
mStatus = NO_ERROR;
mExitPending = false;
mThread = thread_id_t(-1);
// hold a strong reference on ourself
mHoldSelf = this;
mRunning = true;
bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
mRunning = false;
mThread = thread_id_t(-1);
mHoldSelf.clear(); // "this" may have gone away after this.
return UNKNOWN_ERROR;
}
// Do not refer to mStatus here: The thread is already running (may, in fact
// already have exited with a valid mStatus result). The NO_ERROR indication
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
return NO_ERROR;
// Exiting scope of mLock is a memory barrier and allows new thread to run
}
run()函数流程图:
canCallJava作用:
可以看到,上面代码,run函数调用的是_threadLoop()而不是子类重写的threadLoop(),
_threadLoop()作用是什么?
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast(user);
sp strong(self->mHoldSelf);
wp weak(strong);
self->mHoldSelf.clear();
#if defined(__ANDROID__)
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
result = self->threadLoop();
}
} else {
result = self->threadLoop();
}
// establish a scope for mLock
{
Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) {
self->mExitPending = true;
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
// note that interested observers blocked in requestExitAndWait are
// awoken by broadcast, but blocked on mLock until break exits scope
self->mThreadExitedCondition.broadcast();
break;
}
}
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
// And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0);
return 0;
}
_threadLoop()作用:
同步是多线程编程不可回避的问题,安卓提供了一些同步类来解决线程同步的问题,这些同类是只是对系统提
供的多线程同步函数做了封装。
互斥类用于多线程对同一资源的占用相互排斥,一个很形象的比喻是:想象你在飞机上要去上厕所,这时卫生间
的信息牌上显示“有人”,你必须等里面的人出来后才可进去。
Mutex构造函数位于:/system/core/libutils/include/utils/Mutex.h
代码如下:
inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
if (type == SHARED) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&mMutex, &attr);
pthread_mutexattr_destroy(&attr);
} else {
pthread_mutex_init(&mMutex, NULL);
}
}
实际是对系统提供的多线程同步函数进行封装,传入type为SHARE表示该锁跨进程起作用。
加锁、解锁:
inline status_t Mutex::lock() {
return -pthread_mutex_lock(&mMutex);
}
inline void Mutex::unlock() {
pthread_mutex_unlock(&mMutex);
}
inline status_t Mutex::tryLock() {
return -pthread_mutex_trylock(&mMutex);
}
使用Mutex类lock()进行加锁时,需要手动调unlock()进行解锁,如果代码有多个分支调用时,
就需要在每个分支上进行解锁,不然可能出现死锁问题。Mutex类提供一个子类AutoLock,实际自动解锁
功能。AutoLock利用了C++的构造和析构函数,AutoLock定义:
class SCOPED_CAPABILITY Autolock {
public:
inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }
inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }
inline ~Autolock() RELEASE() { mLock.unlock(); }
private:
Mutex& mLock;
// Cannot be copied or moved - declarations only
Autolock(const Autolock&);
Autolock& operator=(const Autolock&);
}
在AutoLock在构造函数实现lock(),析构函数实现unlock(),使用时,先定义一个Mutex对象xlock,再AutoLock autolock(xlock)就可以。
在此autolock对象生命周期内,自动完成加锁和解锁。
多线程同步中,条件类对应的使用场景是:线程A做初始化工作,B、C必须等到初始化工作完成后才能工作,
所以B和C在等待一个条件,是等待者,A是触发者。代码定义如下:
inline Condition::Condition(int type) {
pthread_condattr_t attr;
pthread_condattr_init(&attr);
#if defined(__linux__)
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
#endif
if (type == SHARED) {
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
}
pthread_cond_init(&mCond, &attr);
pthread_condattr_destroy(&attr);
}
inline Condition::~Condition() {
pthread_cond_destroy(&mCond);
}
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
struct timespec ts;
#if defined(__linux__)
clock_gettime(CLOCK_MONOTONIC, &ts);
#else // __APPLE__
// Apple doesn't support POSIX clocks.
struct timeval t;
gettimeofday(&t, NULL);
ts.tv_sec = t.tv_sec;
ts.tv_nsec = t.tv_usec*1000;
#endif
// On 32-bit devices, tv_sec is 32-bit, but `reltime` is 64-bit.
int64_t reltime_sec = reltime/1000000000;
ts.tv_nsec += static_cast(reltime%1000000000);
if (reltime_sec < INT64_MAX && ts.tv_nsec >= 1000000000) {
ts.tv_nsec -= 1000000000;
++reltime_sec;
}
int64_t time_sec = ts.tv_sec;
if (time_sec > INT64_MAX - reltime_sec) {
time_sec = INT64_MAX;
} else {
time_sec += reltime_sec;
}
ts.tv_sec = (time_sec > LONG_MAX) ? LONG_MAX : static_cast(time_sec);
return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
}
inline void Condition::signal() {
pthread_cond_signal(&mCond);
}
inline void Condition::broadcast() {
pthread_cond_broadcast(&mCond);
}
wait():等待事件
waitRelative():等待事件,并设置等待时间,超时没回复就退出
signal():触发者A用来通知条件已经 满足,但是B和C只有一个被唤醒
broadcast():触发者A用来通知条件已经 满足,但是B和C全被唤醒
上面函数调用需要放在一个Mutex的lock()和unlock()内。
实际使用案例:
status_t Thread::requestExitAndWait()
{
Mutex::Autolock _l(mLock);
if (mThread == getThreadId()) {
ALOGW(
"Thread (this=%p): don't call waitForExit() from this "
"Thread object's thread. It's a guaranteed deadlock!",
this);
return WOULD_BLOCK;
}
mExitPending = true;
while (mRunning == true) {
mThreadExitedCondition.wait(mLock);
}
// This next line is probably not needed any more, but is being left for
// historical reference. Note that each interested party will clear flag.
mExitPending = false;
return mStatus;
}
int Thread::_threadLoop(void* user)
{
Thread* const self = static_cast(user);
sp strong(self->mHoldSelf);
wp weak(strong);
self->mHoldSelf.clear();
#if defined(__ANDROID__)
// this is very useful for debugging with gdb
self->mTid = gettid();
#endif
bool first = true;
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
result = self->threadLoop();
}
} else {
result = self->threadLoop();
}
// establish a scope for mLock
{
Mutex::Autolock _l(self->mLock);
if (result == false || self->mExitPending) {
self->mExitPending = true;
self->mRunning = false;
// clear thread ID so that requestExitAndWait() does not exit if
// called by a new thread using the same thread ID as this one.
self->mThread = thread_id_t(-1);
// note that interested observers blocked in requestExitAndWait are
// awoken by broadcast, but blocked on mLock until break exits scope
self->mThreadExitedCondition.broadcast();
break;
}
}
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
// And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0);
return 0;
}
什么是原子操作?
原子操作是最小的执行单位,该操作在执行完毕前,不会受其他任务或事件打断。
g_flags++不是一个原子操作,因为从汇编指令上看,g_flags++操作需要三个汇编指令才能完成:
(1)从内存加载g_flags到寄存器
(2)寄存器数值+1
(3)寄存器数值写回内存
以下代码可以导致问题:
void thread1()
{
g_flag--;
}
void thread2()
{
g_flag++;
}
可以通过Mutex实现原子操作如下:
static Mutex lock;
void thread1()
{
lock.lock();
g_flag--;
lock.unlock();
}
void thread2()
{
lock.lock();
g_flag++;
lock.unlock();
}
但是使用Mutex存在问题:Mutex的使用方法比它所要保护的内容还要复杂,而且使用Mutex需要进入内核态。
安卓提供了一些原子操作的函数,在Atomic.h中:
ANDROID_ATOMIC_INLINE
int32_t android_atomic_inc(volatile int32_t* addr)
{
volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
/* Int32_t, if it exists, is the same as int_least32_t. */
return atomic_fetch_add_explicit(a, 1, memory_order_release);
}
ANDROID_ATOMIC_INLINE
int32_t android_atomic_dec(volatile int32_t* addr)
{
volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
return atomic_fetch_sub_explicit(a, 1, memory_order_release);
}
ANDROID_ATOMIC_INLINE
int32_t android_atomic_add(int32_t value, volatile int32_t* addr)
{
volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
return atomic_fetch_add_explicit(a, value, memory_order_release);
}
ANDROID_ATOMIC_INLINE
int32_t android_atomic_and(int32_t value, volatile int32_t* addr)
{
volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
return atomic_fetch_and_explicit(a, value, memory_order_release);
}
ANDROID_ATOMIC_INLINE
int32_t android_atomic_or(int32_t value, volatile int32_t* addr)
{
volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);
return atomic_fetch_or_explicit(a, value, memory_order_release);
}