安卓C下线程Thread、锁、原子操作

一、安卓C下线程Thread、锁、原子操作

1、使用场景

安卓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<Thread> 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作用:

  • 通过canCallJava,调用线程函数前,会attach到JNI环境,新建线程函数就可以使用JNI函数
  • 线程函数退出后,它会从JNI环境中detach,释放一些资源。

3、为何是_threadLoop()不是threadLoop()

可以看到,上面代码,run函数调用的是_threadLoop()而不是子类重写的threadLoop(),

_threadLoop()作用是什么?


int Thread::_threadLoop(void* user)
{
    Thread* const self = static_cast<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> 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()作用:

  • 首次运行时,调用readToRun()判断是否准备好
  • 判断mExitPending状态是否为ture,mExitPending可以在子线程外部通过requestExit()设置,属于被动退出线程
  • threadLoop()返回false时,主动停止线程

二、安卓C下的锁

同步是多线程编程不可回避的问题,安卓提供了一些同步类来解决线程同步的问题,这些同类是只是对系统提

供的多线程同步函数做了封装。

1、互斥类Mutex

互斥类用于多线程对同一资源的占用相互排斥,一个很形象的比喻是:想象你在飞机上要去上厕所,这时卫生间

的信息牌上显示“有人”,你必须等里面的人出来后才可进去。

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);
}

2、方便的AutoLock

使用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对象生命周期内,自动完成加锁和解锁。

3、条件类Conditon

多线程同步中,条件类对应的使用场景是:线程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<long>(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<long>(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<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);
    wp<Thread> 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);
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值