跨平台(Windows+Linux)的线程辅助程序

如果一个项目需要跨平台,可选择的编程语言就受到一定的限制,如果再考虑运行效率,可选择的语言似乎只能是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  
} 

猜你喜欢

转载自blog.csdn.net/wushuangge/article/details/79230656