今天开始就要开始Obotcha的Thread介绍了。其实多线程这个东东大家平时编程用的一定很多,linux C一般用pthread,C++一般用thread,说实话,为了多线程,我还看了一下《C++并发编程实战》,总的来说还是java的concurrent包提供的功能比较好用。哈哈,所以在Obotcha中专门有一个concurrent文件架来支持多线程。
开发Thread类,主要遇到了下面几个问题:
1.Thread类是智能指针,如果遇到下面的情况,智能指针被释放时,线程该如何处理。例如下面的代码
{
Thread t = createThread();
t->start()
}
t的生命周期结束后,t就会被释放,但是这个时候如果t的线程还在运行,那就会出现无法预料的情况了。但是如果让使用Thread类用户去关心t的生命周期,这显然也是不合理的。所以在这种情况下,我用了一个KeepAliveThread的类来保存所有启动的thread实例。只有当线程运行完成后,从KeepAliveThread中移除,智能指针才会做释放操作。我们看一下Obotcha Thread的start方法做了什么操作:
int _Thread::start() {
....
mStatus = ThreadIdle;
pthread_attr_init(&mThreadAttr);
pthread_create(&mPthread, &mThreadAttr, localRun, this);
while(bootFlag->orAndGet(0) == 0) {
//wait
}
return 0;
}
pthread_create后会有一个bootFlag的循环等待,这个flag会在线程启动后设置,这么做的处理主要是为了防止在线程还未启动的时候有用户直接调stop,导致各种异常的crash。接下来,看一下localRun做了什么处理~
void* _Thread::localRun(void *th) {
_Thread *thread = static_cast<_Thread *>(th);
//thread->mStatus = ThreadRunning;
KeepAliveThread mKAThread = mKeepAliveThread;
sp<_Thread> localThread;
localThread.set_pointer(thread);
mKeepAliveThread->save(localThread);
//wangsl
thread->bootFlag->orAndGet(1);
//wangsl
.....
}
mKeepAliveThread->save(localThread);这句就是将当前thread保存到了keepAliveThread中,这样就能防止thread生命周期到了自动释放的问题了。
2.线程运行结束后,如果触发自动释放
由于之前将线程的实例存放在了keepAliveThread中,所以当线程运行完成后,需要将实例移出keepAliveThread来触发自动释放。Obotcha的做法是当thread运行结束,将对应的pthread_t数值加入到释放队列,同时触发keepAlilveThread去做回收~。相关代码如下:
void* _Thread::localRun(void *th) {
.....
thread->mStatus = ThreadRunning;
if(thread->mRunnable != nullptr) {
thread->mRunnable->run();
thread->mRunnable = nullptr;
} else {
thread->run();
}
......
thread->mStatus = ThreadComplete;
mKAThread->drop(localThread->mPthread);
return nullptr;
}
mKAThread->drop(localThread->mPthread);这句就是将pthread_t加入移除队列。最后我们看一下移除的操作:
void _KeepAliveThread::run() {
//printf("RecyleThread run \n");
//mDestroyMutex->lock();
//if(mDestroyBarrier == 1) {
//mDestroyMutex->unlock();
// return;
//}
ThreadLocal<Thread> tLocal = mThreadLocal;
BlockingQueue<Uint64> mQueue = queue;
//mDestroyMutex->unlock();
while(1) {
mStartBarrier->orAndGet(1);
Uint64 t = mQueue->deQueueFirst();
tLocal->remove(t->toValue());
//can not print thread's infor,because thread may be released!!
}
}
tLocal里面就是存放了所有运行的线程实例,如果线程运行完成,且没有任何引用,那当tLocal->remove之后,thread就会被自动释放了。
开发Thread花了大约10天左右,经历了各种线程异步导致的crash,特别是线程退出这块,是耗费了5天左右才完成所有问题的解决。这个印证了一句名言:编写一个安全退出的程序难道远大于编写一个稳定运行的程序~~。哈哈。
github代码:
https://github.com/wangsun1983/Obotcha/blob/master/util/concurrent/Thread.cpp