(CβΊβΊ) π§΅Thread
in Android Native
#include <stdio.h>
#include <pthread.h>
#include "StrongPointer.h"
#include "RefBase.h"
using namespace android;
void *foo(void* data)
{
printf("foo()\n");
return 0;
}
int main()
{
pthread_t thread;
pthread_create( &thread, 0, foo, 0 );
pthread_join( thread, 0 );
return 0;
}
Androidλ Linux μμ μ¬λΌκ° μμΌλ―λ‘ λ¦¬λ μ€μμμ μ°λ λλΆν° μ΄ν΄λ³΄λ©΄, κΈ°λ³Έμ μΈ POSIX threadλ μμ κ°μ΄ μ¬μ©νλ€.
#include <stdio.h>
#include <pthread.h>
#include "StrongPointer.h"
#include "RefBase.h"
using namespace android;
class Thread
{
pthread_t thread;
public:
virtual void foo(void) = 0;
static void *_foo(void* data)
{
Thread *self = static_cast<Thread*>(data);
self->foo();
return 0;
}
void run() { pthread_create( &thread, 0, _foo, this ); }
void join() { pthread_join( thread, 0 ); }
};
class MyThread : public Thread
{
public:
void foo(void)
{
printf("MyThread::foo()\n");
}
};
int main()
{
MyThread thread;
thread.run();
thread.join();
return 0;
}
μ΄κ²μ ν΄λμ€λ‘ wrappingνλ©΄ μ΄λ κ²λ μΈ μ μμκ²μ΄λ€. λ§μ°¬κ°μ§λ‘ Androidμμλ POSIX threadλ₯Ό wrapν΄μ ꡬνν΄λμλλ°,
#ifndef _LIBS_UTILS_THREAD_H
#define _LIBS_UTILS_THREAD_H
#include <stdint.h>
#include <sys/types.h>
#include <time.h>
#if !defined(_WIN32)
# include <pthread.h>
#endif
#include "Condition.h"
#include "Errors.h"
#include "Mutex.h"
#include "RefBase.h"
#include "Timers.h"
#include "ThreadDefs.h"
// βββββββββββββββββββββββββββββββββββββ
namespace android {
// βββββββββββββββββββββββββββββββββββββ
// DO NOT USE: please use std::thread
class Thread : virtual public RefBase
{
public:
// Create a Thread object, but doesnβt create or start the associated
// thread. See the run() method.
explicit Thread(bool canCallJava = true);
virtual ~Thread();
// Start the thread in threadLoop() which needs to be implemented.
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
// Ask this objectβs thread to exit. This function is asynchronous, when the
// function returns the thread might still be running. Of course, this
// function can be called from a different thread.
virtual void requestExit();
// Good place to do one-time initializations
virtual status_t readyToRun();
// Call requestExit() and wait until this objectβs thread exits.
// BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
// this function from this objectβs thread. Will return WOULD_BLOCK in
// that case.
status_t requestExitAndWait();
// Wait until this objectβs thread exits. Returns immediately if not yet running.
// Do not call from this objectβs thread; will return WOULD_BLOCK in that case.
status_t join();
// Indicates whether this thread is running or not.
bool isRunning() const;
#if defined(__ANDROID__)
// Return the threadβs kernel ID, same as the thread itself calling gettid(),
// or -1 if the thread is not running.
pid_t getTid() const;
#endif
protected:
// exitPending() returns true if requestExit() has been called.
bool exitPending() const;
private:
// Derived class must implement threadLoop(). The thread starts its life
// here. There are two ways of using the Thread object:
// 1) loop: if threadLoop() returns true, it will be called again if
// requestExit() wasnβt called.
// 2) once: if threadLoop() returns false, the thread will exit upon return.
virtual bool threadLoop() = 0;
private:
Thread& operator=(const Thread&);
static int _threadLoop(void* user);
const bool mCanCallJava;
// always hold mLock when reading or writing
thread_id_t mThread;
mutable Mutex mLock;
Condition mThreadExitedCondition;
status_t mStatus;
// note that all accesses of mExitPending and mRunning need to hold mLock
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
#if defined(__ANDROID__)
// legacy for debugging, not used by getTid() as it is set by the child thread
// and so is not initialized until the child reaches that point
pid_t mTid;
#endif
};
} // namespace android
// βββββββββββββββββββββββββββββββββββββ
#endif // _LIBS_UTILS_THREAD_H
// βββββββββββββββββββββββββββββββββββββ
μ΄κ²μ κ² κ΅¬νμ¬νμ λ§μ§λ§ κΈ°λ³Έμ μΈ λμμ μΈν°νμ΄μ€λ§ λ΄λ μ§μν μ μλ€. μ£Όμν λ§ν μ¬νμ μ°λ λ κ°μ²΄λ₯Ό λ΄λΆμμ strong pointerλ‘ λ§λ λ€, refCountλ₯Ό μ체μ μΌλ‘ 1 κ°μμν¨λ€λ μ μ΄λ€. κ·Έλμ μΌλ° ν¬μΈν°λ‘ Android μ°λ λλ₯Ό μ¬μ©νλ©΄ refCountκ° 0μ΄ λμ΄λ²λ¦¬λ―λ‘ μ μμ μΌλ‘ λμ§μλλ€.
μ¦, λ°λμ spλ₯Ό μ¨μ μ¬μ©ν΄μΌνλ€.
as below
using namespace android;
class MyThread : public Thread
{
public:
bool threadLoop()
{
printf("MyThread::foo()\n");
sleep(2);
return true;
}
};
int main()
{
sp<Thread> thread = new MyThread;
thread->run("MyThread");
thread->join();
return 0;
}
λ€μμΌλ‘ μ΄ν΄λ³Ό λ΄μ©μ Androidμμ μ 곡νλ Mutex
μ΄λ€.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int sum = 0;
class MyThread : public Thread
{
public:
bool threadLoop()
{
int local;
for(int i=0; i<10000000; i++)
{
pthread_mutex_lock(&mutex);
local = sum;
local = local + 1;
sum = local;
pthread_mutex_unlock(&mutex);
}
return false;
}
};
int main()
{
sp<Thread> thread1 = new MyThread;
thread1->run("MyThread1");
sp<Thread> thread2 = new MyThread;
thread2->run("MyThread2");
thread1->join();
thread2->join();
printf("sum = %d\n", sum );
return 0;
}
μμλ POSIX mutexλΆν° μ΄ν΄λ³΄λ©΄ μμ κ°μ΄ μ¬μ©νλ€.
using namespace android;
Mutex mutex;
int sum = 0;
class MyThread : public Thread
{
public:
bool threadLoop()
{
int local;
for(int i=0; i<10000000; i++)
{
mutex.lock();
local = sum;
local = local + 1;
sum = local;
mutex.unlock();
}
return false;
}
};
κ·Έλ¦¬κ³ λ νΈνκ² μ¬μ©νκΈ° μν΄ μμλ Androidμμ wrapper ν΄λμ€λ₯Ό μ 곡νλλ° λ€λ§ μ¬κΈ°μ μ£Όμν μ¬νμ΄ μλ€. μ΄ μκ³μμ(Critical section)μμ μμΈλ°μμ unlockμ΄ μλλ λ¬Έμ κ° μμ μ μλ€λκ²μ΄λ€. κ·Έλ λ€κ³ λͺ¨λ catchμμ unlockμ νλ κ²λ λκ° λΆν©λ¦¬νλ€.
κ·Έλμ Androidμμ autolock
μ΄λΌλ κ²μ μ 곡μ νλ€. μ΄κ²λ μμ RAII κ°λ
(μμμ κ΄λ¦¬λ κ°μ²΄)μ΄λ€.
Mutex mutex;
int sum = 0;
class MyThread : public Thread
{
public:
bool threadLoop()
{
int local;
for(int i=0; i<10000000; i++)
{
Mutex::Autolock ll(mutex);
local = sum;
local = local + 1;
sum = local;
}
return false;
}
};
Autolockμ μμΈκ° λ°μνλλΌλ μλμΌλ‘ unlockμ ν΄μ€λ€. (call stackμ μλ¨λβ¦?)
μ΄λ²μ Conditional
μ λν΄ μμ보μ. μΌλ°μ μΈ μμ°μ μλΉμ λͺ¨λΈμ΄ μμλ κ°κ°μ΄ λ³κ°μ μ°λ λμμ λμνλ€λ©΄,
μμ°λ μνλλ° μλΉμκ° μλνλ λ¬Έμ κ° μμ μ μλ€. μΌμ μμκ° μλ κ²μ΄λ€. μ΄λ° λ¬Έμ λ₯Ό μν΄ POSIXλ pthread_cond_wait, pthread_cond_signal λ±μ apiλ₯Ό μ 곡νλ€.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
class Producer: public Thread
{
public:
bool threadLoop()
{
pthread_mutex_lock(&mutex);
for(int i=0; i<3; i++)
{
sleep(1);
printf("μμ°μ€β¦\n");
}
printf("μμ°μλ£\n");
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return false;
}
};
class Consumer : public Thread
{
public:
bool threadLoop()
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
for(int i=0; i<3; i++)
{
sleep(1);
printf("μλΉμ€β¦\n");
}
printf("μλΉμλ£\n");
pthread_mutex_unlock(&mutex);
return false;
}
};
int main()
{
sp<Thread> pro = new Producer;
pro->run("Producer");
sp<Thread> con = new Consumer;
con->run("Consumer");
pro->join();
con->join();
return 0;
}
κ·Έλ¦¬κ³ μμλ Androidμμ μ 곡νλ classκ° μ‘΄μ¬νλ€.
using namespace android;
Mutex mutex;
Condition cond;
class Producer: public Thread
{
public:
bool threadLoop()
{
Mutex::Autolock ll(mutex);
for(int i=0; i<3; i++)
{
sleep(1);
printf("μμ°μ€β¦\n");
}
printf("μμ°μλ£\n");
cond.signal();
return false;
}
};
class Consumer : public Thread
{
public:
bool threadLoop()
{
Mutex::Autolock ll(mutex);
cond.wait(mutex);
for(int i=0; i<3; i++)
{
sleep(1);
printf("μλΉμ€β¦\n");
}
printf("μλΉμλ£\n");
return false;
}
};
νΉμ΄ν μ μ signal()
κ³Ό broadcast()
κ° μλ€λ μ μ΄λ€. broadcast APIλ μ¬λ¬ μ°λ λμκ² μ νΈλ₯Ό λ³΄λΌ μ μμ΄ νΈλ¦¬νλ€.