如果一个项目需要跨平台,可选择的编程语言就受到一定的限制,如果再考虑运行效率,可选择的语言似乎只能是C/C++了。与Java不同,C/C++是被操作系统“原生态”支持的,因而各操作系统实现的方式也略有不同,各自做了不同的扩展,而不象Java一样有统一库函数及接口。 用C/C++做跨平台开发需要考虑操作系统的不同,当然,有别人写好的跨平台库,这当中,最著名的就是boost了。本文不是讨论boost的文章,笔者认为,如果开发难度不大,自己开发也不错,可以充分了解各操作系统的特点,因此,本文涉及的是自己写一些简单的跨平台的东西。 因为项目的性质,需要多线程,因此首先需要解决的是多线程方面的编程,这里,涉及两方面内容: 1.线程的创建(启动)与停止。 2.全局数据读写的同步(即互斥量的应用)。 一、Windows下的线程函数和互斥量 Windows下的线程函数有多种表达(这跟windows的发展历史有关或是跟windows的开发接口习惯有关,Windows的开发接口总是形式多样,以下互斥量也是如此)。MFC中有AfxCreateThread,AfxEndThread等函数,Win32API中有CreateThread,ExitThread等,随VC一起的有_beginThread和_endThread等。直接使用Win32 API中的线程函数,但又在在线程中调用某些CRT中的函数,就会有一点小的内存泄露(http://support.microsoft.com/kb/104641/),因此建议采用_beginThread、_endThread来创建和中止线程(本文不考虑用MFC库)。 Windows下的线程或进程间同步的手段大体上有 event, mutex, semaphore, timer object这几类。 event:一个线程等待信号,另一个线程发出信号。 semaphore:一个线程或多个线程占有资源,其它线程等待,即某个资源同时只能被n个线程占有,第n+1个线程只能等待。 mutex:是semaphore的一个特殊情况,一个线程占有资源,另外的线程等待资源。 大部分情况下,mutex已能满足我们的要求,所以本文也只涉及了mutex。 二、Linux下的线程函数和互斥量 Linux下的线程函数为pthread_create和pthread_exit,比较简单,需要注意的是,线程入口函数略有不同,pthread_create要求的线程函数为 void * (* proc)( void * ),而_beginThread要求的线程函数为void (* proc)( void * ),即返回值不同,为统一化,只能舍弃Linux下的线程函数的返回值。好在如果需要线程函数的返回值,可以定义一个全局变量来解决此问题。 Linux下的线程同步手段比较单一,有mutex和条件变量。互斥量操的函数为pthread_mutex_init,pthread_mutex_destroy等,条件变量类似于event,但需要与互斥量共同作用(如果是微软,可能会将二者合起来,这是微软一贯的风格)。同样需要注意的是,Linux中的互斥量没有名称,也就是它只能采用全局变量这种方式,并且不能运用到进程,而windows下的互斥量可以是命名互斥量,也可用于进程间同步。 Linux和Windows另一个不同是等待函数,Windows下的等待函数有两个:WaitForSingleObject和WaitForMultipleObjects,分别用于等待一个同步量和多个同步量,并有“超时”的概念,Linux下的等待函数有pthread_mutex_lock和pthread_mutex_trylock,只能等待一个同步量,没有“超时”的概念,为实现“超时”,本文采用一个简单的办法(见下面的实现)。 从Windows和Linux编程接口函数的比较,可以看出微软的接口确实比较“贴心”,但有时也会造成接口函数过多,刚开始时无所适从,例如C#的线程同步,方式多得使人眼花。 三、线程辅助程序代码 说明文件(lightthread.h)代码。 [cpp] view plain copy #ifndef _LIGHTTHREAD_H_ #define _LIGHTTHREAD_H_ #include "platform.h" #if defined(_WIN32_PLATFORM_) typedef void * Mutex_Handle; #endif #if defined(_LINUX_PLATFORM_) typedef pthread_mutex_t Mutex_Handle; #endif class CLightThread { public: CLightThread() {}; ~CLightThread() {}; static int CreateThread(void ( *proc )( void * ), void *pargs); static void EndThread(); static unsigned int GetCurrentThreadId(); static void Sleep(unsigned int milliseconds); static void DiscardTimeSlice(); }; class CLightThreadMutex { public: CLightThreadMutex(); ~CLightThreadMutex(); int Lock(); int TryLock(unsigned int dwMilliseconds); void Unlock(); private: Mutex_Handle m_hMutex; }; typedef struct { unsigned int errorno; char errormsg[512]; } thread_error_t; class CThreadError { typedef struct { thread_error_t threaderror; unsigned int threadid; void * next; } internal_thread_error_t; public: CThreadError(); ~CThreadError(); void operator=(int errorno); void operator=(const char * msg); void operator=(thread_error_t & st); unsigned int GetLastErrorNo(); const char *GetLastErrorMsg(); const thread_error_t *GetLastErrorStruct(); private: internal_thread_error_t* m_pStart; internal_thread_error_t* allocMemory(unsigned int tid); internal_thread_error_t * search(unsigned int tid); }; #endif 实现文件(lightthread.cpp)代码。 [cpp] view plain copy #include <stdio.h> #include <time.h> #include <string.h> #include "lightthread.h" #if defined(_WIN32_PLATFORM_) #include <windows.h> #include <process.h> /* _beginthread, _endthread */ #define gxstrcpy(d,n,s) strcpy_s(d,n,s) #endif #if defined(_LINUX_PLATFORM_) #include <pthread.h> #include <unistd.h> #include <sys/time.h> #include <stdio.h> #define gxstrcpy(d,n,s) strncpy(d,s,n) #define THREAD_IDLE_TIMESLICE_MS 20 #endif #define GX_UNDEFINED 0xffffffff #define GX_S_OK 0x00000000 #include "TimeSpan.h" #if defined(_LINUX_PLATFORM_) typedef struct { void (* proc)( void * ) ; void * pargs; } _threadwraper_linux_t; void * _ThreadWraper_Linux(void *pargs) { _threadwraper_linux_t *pth= (_threadwraper_linux_t *)pargs; pth->proc(pth->pargs); delete[] pth; return NULL; } int CLightThread::CreateThread(void ( *proc )( void * ), void *pargs) { pthread_t ntid; _threadwraper_linux_t* pthreadwraper = new _threadwraper_linux_t[1]; pthreadwraper[0].proc = proc; pthreadwraper[0].pargs = pargs; return pthread_create(&ntid, NULL, _ThreadWraper_Linux, pthreadwraper); } void CLightThread::DiscardTimeSlice() { usleep(THREAD_IDLE_TIMESLICE_MS*1000); } void CLightThread::EndThread() { pthread_exit(NULL); } unsigned int CLightThread::GetCurrentThreadId() { return pthread_self(); } void CLightThread::Sleep(unsigned int milliseconds) { if(milliseconds>=1000) { unsigned int s = milliseconds/1000; unsigned int us = milliseconds - s*1000; sleep(s); if(us>0) usleep(us*1000); } else { usleep(milliseconds*1000); } } //===================================================================================== CLightThreadMutex::CLightThreadMutex() { pthread_mutex_init(&m_hMutex, NULL); } CLightThreadMutex::~CLightThreadMutex() { pthread_mutex_destroy(&m_hMutex); } int CLightThreadMutex::Lock() { return pthread_mutex_lock(&m_hMutex) == 0 ?0:-1; } int CLightThreadMutex::TryLock(unsigned int dwMilliseconds) { // The function pthread_mutex_trylock() returns zero if a lock on the mutex object referenced by mutex is acquired. Otherwise, an error number is returned to indicate the error. unsigned int us= dwMilliseconds*1000; int rt = pthread_mutex_trylock(&m_hMutex); if( rt == EBUSY) { CMyTimeSpan start; while(rt == EBUSY) { if( start.GetSpaninMilliseconds()>dwMilliseconds) { rt = -1; } else { usleep(20000); //sleep 20ms rt = pthread_mutex_trylock(&m_hMutex); } } } return rt; } void CLightThreadMutex::Unlock() { pthread_mutex_unlock(&m_hMutex); } #endif #if defined(_WIN32_PLATFORM_) int CLightThread::CreateThread(void( *proc )( void * ), void *pargs) { return _beginthread( proc, 0, pargs ); } void CLightThread::EndThread() { _endthread(); } unsigned int CLightThread::GetCurrentThreadId() { return ::GetCurrentThreadId(); } void CLightThread::Sleep(unsigned int miniseconds) { ::Sleep(miniseconds); } void CLightThread::DiscardTimeSlice() { ::SwitchToThread(); } //===================================================================================== //===================================================================================== CLightThreadMutex::CLightThreadMutex() { m_hMutex = CreateMutexA(NULL,FALSE,NULL); } CLightThreadMutex::~CLightThreadMutex() { if(m_hMutex) CloseHandle(m_hMutex); } int CLightThreadMutex::Lock() { if( m_hMutex && WaitForSingleObject(m_hMutex, INFINITE)==WAIT_OBJECT_0) return 0; return -1; } int CLightThreadMutex::TryLock(unsigned int dwMilliseconds) { if( m_hMutex&& WaitForSingleObject(m_hMutex, dwMilliseconds) ==WAIT_OBJECT_0) return 0; return -1; } void CLightThreadMutex::Unlock() { if(m_hMutex) ReleaseMutex(m_hMutex); } #endif //===================================================================================================== CThreadError::CThreadError() { m_pStart = NULL; } CThreadError::~CThreadError() { internal_thread_error_t *temp; while(m_pStart) { temp = m_pStart; m_pStart = (internal_thread_error_t*)m_pStart->next; delete temp; } } void CThreadError::operator=(int errorno) { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); if(!temp) { temp = allocMemory(tid); } temp->threaderror.errorno = errorno; temp->threaderror.errormsg[0] = '\0'; } void CThreadError::operator=(const char * msg) { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); if(!temp) { temp = allocMemory(tid); } temp->threaderror.errorno = GX_UNDEFINED; gxstrcpy(temp->threaderror.errormsg, 510, msg); } void CThreadError::operator=(thread_error_t & st) { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); if(!temp) { temp = allocMemory(tid); } memcpy(&temp->threaderror, &st, sizeof(thread_error_t)); } unsigned int CThreadError::GetLastErrorNo() { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); return temp?temp->threaderror.errorno:GX_S_OK; } const char *CThreadError::GetLastErrorMsg() { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); return temp?(const char*)temp->threaderror.errormsg:NULL; } const thread_error_t *CThreadError::GetLastErrorStruct() { unsigned int tid = CLightThread::GetCurrentThreadId(); internal_thread_error_t *temp = search(tid); return temp?(const thread_error_t *)(&(temp->threaderror)):NULL; } CThreadError::internal_thread_error_t* CThreadError::allocMemory(unsigned int tid) { internal_thread_error_t *temp = new internal_thread_error_t; temp->threadid = tid; temp->next = m_pStart; m_pStart = temp; return temp; } CThreadError::internal_thread_error_t * CThreadError::search(unsigned int tid) { internal_thread_error_t *temp = m_pStart; while(temp) { if(temp->threadid == tid) break; temp->next = (void *)temp; } return temp; } 上面代码中,在不同的平台下,需要定义一个宏,_LINUX_PLATFROM_或_WIN32_PLATFROM_,对于Linux和Windows平台。CThreadError是一个线程错误类,保存了当前线程的最后一次错误值和描述。Linux版线程实现函数的TryLock中,用到一个CMyTimeSpan类,它的作用是计时,其实现代码如下: [cpp] view plain copy #include "platform.h" #if defined(_WIN32_PLATFORM_) #include <Windows.h> #define timelong_t ULARGE_INTEGER #endif #if defined(_LINUX_PLATFORM_) #include <sys/time.h> #include <linux/errno.h> #define timelong_t struct timeval #endif class CMyTimeSpan { public: CMyTimeSpan(); void Reset(); unsigned long long GetSpaninMicroseconds(); unsigned int GetSpaninMilliseconds(); unsigned int GetSpaninSeconds(); private: timelong_t m_start; void getCurrentTimeLong(timelong_t *tl); }; //===================================================================================== CMyTimeSpan::CMyTimeSpan() { getCurrentTimeLong(&m_start); } void CMyTimeSpan::Reset() { getCurrentTimeLong(&m_start); } unsigned int CMyTimeSpan::GetSpaninMilliseconds() { return (unsigned int)(GetSpaninMicroseconds()/1000LL); } unsigned int CMyTimeSpan::GetSpaninSeconds() { return (unsigned int)(GetSpaninMicroseconds()/1000000LL); } unsigned long long CMyTimeSpan::GetSpaninMicroseconds() { timelong_t end; getCurrentTimeLong(&end); #if defined(_WIN32_PLATFORM_) return (end.QuadPart - m_start.QuadPart)/10; #endif #if defined(_LINUX_PLATFORM_) return 1000000LL * ( end.tv_sec - m_start.tv_sec ) + end.tv_usec - m_start.tv_usec; #endif } void CMyTimeSpan::getCurrentTimeLong(timelong_t *tl) { #if defined(_WIN32_PLATFORM_) FILETIME ft; GetSystemTimeAsFileTime(&ft); tl->HighPart= ft.dwHighDateTime; tl->LowPart = ft.dwLowDateTime; #endif #if defined(_LINUX_PLATFORM_) gettimeofday( tl, NULL); #endif }
跨平台(Windows+Linux)的线程辅助程序
猜你喜欢
转载自blog.csdn.net/wushuangge/article/details/79230656
今日推荐
周排行