EPICS多线程编程的库文件解析及程序测试实例

这篇具有很好参考价值的文章主要介绍了EPICS多线程编程的库文件解析及程序测试实例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1)条件信号量的头文件epicsEvent.h

/**文件: epicsEvent.h
 *
 * 简述:epicsEvent二进制信号量的APIs 
 *
 * 为一个简单的二进制信号量定义C++和C API。如果多个线程正在等待相同的事件,当这个事件信号
 * 被发出时,它们中仅一个被唤醒。
 * 事件信号量的主要使用是用于线程同步。使用一个事件信号量的一个示例是处理来自一个或多个生产者线程请求的
 消费者线程。
 * 例如:当创建消费者线程也创建一个epicsEvent
 *
 * 代码:
   epicsEvent event;
 
 * 消费者线程代码包含:
       while(1) {
           pevent.wait();
           while( {more work} ) {
               {process work}
           }
       }
 * 生产者创建请求并且发出语句:
 代码:
       pevent.trigger();
 **/

#ifndef epicsEventh
#define epicsEventh

#include "libComAPI.h"

/** 简述:与C API一起使用的epicsEvent的标识符  */
typedef struct epicsEventOSD *epicsEventId;

/** 简述:从若干C API例程返回状态. */
typedef enum {
    epicsEventOK = 0,
    epicsEventWaitTimeout,
    epicsEventError
} epicsEventStatus;

/** 简述: 为后向兼容性提供的旧名 */
#define epicsEventWaitStatus epicsEventStatus
/** 简述: 为后向兼容性提供的旧名  */
#define epicsEventWaitOK epicsEventOK
/** 简述: 为后向兼容性提供的旧名 */
#define epicsEventWaitError epicsEventError

/** 简述:一个新的epicsEvent的可能初始状态 */
typedef enum {
    epicsEventEmpty,
    epicsEventFull
} epicsEventInitialState;

#ifdef __cplusplus

/**简述:一个二进制信号量 
 * 一个epicsEvent是一个二进制信号量,它可以是空或者满。
 * 当空时,在下次调用trigger()前发出的wait()将阻塞。
 * 当满时,下次调用wait()将清空这个事件并且立即返回。在wait()调用之间可能出现多个trigger()
 * 调用,但将与单个trigger()相同的作用,填充这个事件。
  **/
class LIBCOM_API epicsEvent {
public:
    /**简述: 构造器.
     * 参数: 在创建时,初始状态,空(默认)或满.
     **/
    epicsEvent ( epicsEventInitialState initial = epicsEventEmpty );
    /**简述: 销毁这个epicsEvent和它持有的任何资源 . No calls to
     *  在进行这个调用时,对wait()的调用不再有效。
     **/
    ~epicsEvent ();
    /**简述: 触发这个事件,即,确保下次或当前对wait的调用结束。 
    * 这个方法可以被从vxWorks或RTEMS中断处理程序调用。
     **/
    void trigger ();
    /**简述:Signal是trigger()的同义词.
     **/
    void signal () { this->trigger(); }
    /**简述:等待事件
     * 注意:阻塞到满 .
     **/
    void wait ();
    /**简述:等待事件或者直到指定超时时间.
     * 参数: timeout 以秒为单位的超时延时。一个值为0的超时时间等价于调用tryWait();
     * NaN或任何太大的值被传给目标OS等价于没有超时时间。
     * 返回:如果事件被触发,True,如果它超时了,False
     **/
    bool wait ( double timeout );
    /** 简述: 类似于wait(), 除了事件当前为空,调用立即返回。
    * 返回: 如果事件满(被触发),True,如果空,False .
     **/
    bool tryWait ();
    /**简述:显示这个信号量的信息
     * 注意: 显示的信息是取决于架构的
     * 参数: level 一个无符号int, 用于要显示信息的级别
     **/
    void show ( unsigned level ) const;

    class invalidSemaphore;         /* 异常负载 */
private:
    epicsEvent ( const epicsEvent & );
    epicsEvent & operator = ( const epicsEvent & );
    epicsEventId id;
};

extern "C" {
#endif /*__cplusplus */

/**简述: 从C代码创建要用的epcisEvent,或者返回NULL.
 *
 * 参数: initialState :起始状态epicsEventEmpty或epicsEventFull.
 * 返回:一个新事件的标识符,如果不能创建一个epicsEventId,返回NULL 
 **/
LIBCOM_API epicsEventId epicsEventCreate(
    epicsEventInitialState initialState);

/**简述: 从C代码创建要用的epcisEvent
 *如果不能创建一个epicsEventId对象,这个程序不返回。
* 参数: initialState :起始状态epicsEventEmpty或epicsEventFull.
 * 返回:一个新事件的标识符,如果不能创建一个epicsEventId
 **/
LIBCOM_API epicsEventId epicsEventMustCreate (
    epicsEventInitialState initialState);

/**简述: 销毁一个epicsEvent以及它持有的任何资源
 * 当进行这个调用时,对任何epicsEventWait例程的调用都无效。
 * 参数: id :事件标识符
 **/
LIBCOM_API void epicsEventDestroy(epicsEventId id);

/**简述:触发一个事件,即,确保下次或当前wait调用结束。 
 * 注意: 可以从vxWorks或RTEMS中断处理程序被调用。
 * 参数: id 事件标识符
 * 返回: Status 返回状态
 **/
LIBCOM_API epicsEventStatus epicsEventTrigger(
    epicsEventId id);

/**简述:触发一个事件 .
 * 如果标识符无效,这个例程不返回。
 * 参数 id :事件标识符
 */
LIBCOM_API void epicsEventMustTrigger(epicsEventId id);

/**简述: epicsEventTrigger()的同义词
 * 参数: ID 事件标识符
 * 返回: Status 返回状态
 **/
#define epicsEventSignal(ID) epicsEventMustTrigger(ID)

/**简述: 等待一个事件.
 * 注意:阻塞到满 .
 * 参数: id 事件标识符.
 * 返回: Status 返回状态.
 **/
LIBCOM_API epicsEventStatus epicsEventWait(
    epicsEventId id);

/**简述: 等待一个事件
 * 如果标识符无效,这个例程不返回
 * 参数 id 事件标识符
 */
LIBCOM_API void epicsEventMustWait(epicsEventId id);

/**简述:等待一个事件或者直到指定超时事件耗尽
 * 注意:阻塞直到满或者超时
 * 参数:  id 事件标识符
 * 参数: timeout 以秒为单位的超时时间。一个值0的超时等价于调用epicsEventTryWait(). 
 * ; NaN或者任何太大的值被传递给目标OS等价于没有超时时间。
 * 返回: Status 返回状态.
 **/
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout(
    epicsEventId id, double timeout);

/**简述: 类似于wait(),除了如果事件当前空,调用将带状态epicsEventWaitTimeout立即返回
 * 参数: id 事件标识符
 * 返回: Status指示符, 当事件为空时,epicsEventWaitTimeout.
 **/
LIBCOM_API epicsEventStatus epicsEventTryWait(
    epicsEventId id);

/**简述: 显示这个信号量的信息.
 * 注意:显示的信息是取决于架构的
 * 参数: id 事件标识符
 * 参数: level 一个无符号int,用于要显示信息的级别
LIBCOM_API void epicsEventShow(
    epicsEventId id, unsigned int level);

#ifdef __cplusplus
}
#endif /*__cplusplus */

#include "osdEvent.h"

#endif /* epicsEventh */

2)互斥信号量的头文件 epicsMutex.h

/**文件: epicsMutex.h
 *
 * 简述:用于epicsMutex互斥信号量的APIs
 * 互斥信号量是用于需要独占性访问资源的情况。
 * 一个epicsMutex可以被递归地所有,即,被一个线程多次获取,此线程必须按它被获取次数释放它。
 递归用法对于在处理独占性资源时相互调用的一组例程是常见的。
 *
 * 互斥锁信号量的典型C++用法是:

     epicsMutex lock;
     ...
     ...
     {
         epicsMutex::guard_t G(lock); // lock
         // process resources
     } // unlock
     // or for compatibility
     {
         epicsGuard<epicsMutex> G(lock); // lock
         // process resources
     } // unlock

 *
 * 实现:
 *   - 必须实现递归锁定
 *   - 如果可能,应该实现优先级继承和删除安全性。
 **/
#ifndef epicsMutexh
#define epicsMutexh

#include "epicsAssert.h"

#include "libComAPI.h"

/**简述: 与C API一起使用的epicsMutex的标识符 */
typedef struct epicsMutexParm *epicsMutexId;

/** 从某些C API例程返回的状态。. */
typedef enum {
    epicsMutexLockOK = 0,
    epicsMutexLockTimeout,
    epicsMutexLockError
} epicsMutexLockStatus;

#ifdef __cplusplus

#include "compilerDependencies.h"
#include "epicsGuard.h"

/**简述: 用当前文件和行号创建一个C++ epicsMutex 
 */
#define newEpicsMutex new epicsMutex(__FILE__,__LINE__)

/**简述:用于epicsMutex的C++ API .
 */
class LIBCOM_API epicsMutex {
public:
    typedef epicsGuard<epicsMutex> guard_t;
    typedef epicsGuard<epicsMutex> release_t;
    class mutexCreateFailed; /*异常负载*/
    class invalidMutex; /* 异常负载  */

#if !defined(__GNUC__) || __GNUC__<4 || (__GNUC__==4 && __GNUC_MINOR__<8)
    /**简述: 创建一个互斥量**/
    epicsMutex ();

    /**简述: 用资源位置创建一个互斥量.
     *
     * 注意: newEpicsMutex宏简化了使用这个构造器 .
     * 参数:1)*pFileName:源文件名 
     *       2) lineno :源文件行号
     **/
    epicsMutex ( const char *pFileName, int lineno );
#else
    /**简述:创建一个互斥量 .
     参数:  1)*pFileName:源文件名 
     *       2) lineno :源文件行号
     **/
    epicsMutex ( const char *pFileName = __builtin_FILE(), int lineno = __builtin_LINE() );
#endif

    /**简述:删除这个信号量和其资源.
     **/
    ~epicsMutex ();

    /**简述:显示这个信号量的信息.
     *
     * 注意:结果是依赖架构的.
     *
     * 参数: level 想要报告的信息级别
     **/
    void show ( unsigned level ) const;

    /**简述:索要这个信号量,如果当前被一个不同线程所有,等待到其释放 。
     * 这个调用阻塞,直到调用线程可以独占性访问这个信号量
     * 注意: 在一次成功lock()后,其它递归锁定可能由相同线程发出,但每个必须有一个相关联的unlock()
     **/
    void lock ();

    /**简述:释放这个信号量
     * 注意: 如果一个线程发出了递归锁定,它可以调用与其调用lock()次数相同的unlock()
     **/
    void unlock ();

    /** 简述: 类似lock(),除了如果信号量当前被另一个线程所有,这个调用立即返回,返回false值。
  
     * 返回:如果资源现在被调用者所有,返回True。如果某个其它线程已经所有这个资源,返回false
     **/
    bool tryLock ();
private:
    epicsMutexId id;
    epicsMutex ( const epicsMutex & );
    epicsMutex & operator = ( const epicsMutex & );
};

/**简述: 一个用于在C++代码中定位死锁的信号量 */
class LIBCOM_API epicsDeadlockDetectMutex {
public:
    typedef epicsGuard<epicsDeadlockDetectMutex> guard_t;
    typedef epicsGuard<epicsDeadlockDetectMutex> release_t;
    typedef unsigned hierarchyLevel_t;
    epicsDeadlockDetectMutex ( unsigned hierarchyLevel_t );
    ~epicsDeadlockDetectMutex ();
    void show ( unsigned level ) const;
    void lock (); /* 在成功前,阻塞 */
    void unlock ();
    bool tryLock (); /* 如果成功,true */
private:
    epicsMutex mutex;
    const hierarchyLevel_t hierarchyLevel;
    class epicsDeadlockDetectMutex * pPreviousLevel;
    epicsDeadlockDetectMutex ( const epicsDeadlockDetectMutex & );
    epicsDeadlockDetectMutex & operator = ( const epicsDeadlockDetectMutex & );
};

#endif /*__cplusplus*/

#ifdef __cplusplus
extern "C" {
#endif /*__cplusplus*/

/**简述: 从C代码创建一个要用的epicsMutex信号量 .
 * 这个宏在mutex中存储创建调用的资源位置。
 * 返回: mutex的标识符,或者如果不能被创建,返回NULL
 **/
#define epicsMutexCreate() epicsMutexOsiCreate(__FILE__,__LINE__)
/**简述: 内部API,由epicsMutexCreate()使用 */
LIBCOM_API epicsMutexId epicsStdCall epicsMutexOsiCreate(
    const char *pFileName,int lineno);

/**简述: 从C代码创建一个要用的epicsMutex信号量
 * 这个宏在mutex中存储这个创建调用的资源位置。
 * 如果不能创建这个对象,这个例程不返回。
 * 返回: mutex的标识符
 **/
#define epicsMutexMustCreate() epicsMutexOsiMustCreate(__FILE__,__LINE__)
/**简述:内部API,由epicsMutexMustCreate()使用 */
LIBCOM_API epicsMutexId epicsStdCall epicsMutexOsiMustCreate(
    const char *pFileName,int lineno);

/** 简述:销毁一个epicsMutex信号量 
 * 参数: id 互斥量标识符.
 **/
LIBCOM_API void epicsStdCall epicsMutexDestroy(epicsMutexId id);

/**简述:释放信号量 
 * 参数: id 互斥量标识符
 * 注意: 如果要给线程发出递归锁,它必须调用与其调用epicsMutexLock()数目相同的epicsMutexUnlock()
 **/
LIBCOM_API void epicsStdCall epicsMutexUnlock(epicsMutexId id);

/**简述:索要这个信号量,如果其被一个不同线程所有,等待到其释放 
 * 这个掉阻塞这个调用线程直到能够独占性访问这个信号量。
 * 注意: 在成功lock()后,更多的递归锁可以被这个相同线程发出,但每个锁有一个相关联的unlock()。
 * 参数:  id  互斥锁标识符
 * 返回:  Status 返回状态。
 **/
LIBCOM_API epicsMutexLockStatus epicsStdCall epicsMutexLock(
    epicsMutexId id);

/**简述: 索要一个信号量 
 * 如果返回标识符无效,这个例程不返回。
 *  ID 互斥量标识符
 **/
#define epicsMutexMustLock(ID) {                        \
    epicsMutexLockStatus status = epicsMutexLock(ID);   \
    assert(status == epicsMutexLockOK);                 \
}

/**简要:类似epicsMutexLock(),除了其此调用立即返回,
  带一个表示信号量是否被这个线程或其它线程所有的返回状态。
 *
 * 返回: epicsMutexLockOK 如果资源现在被这个调用者所有.
 *        epicsMutexLockTimeout 如果某个其它线程所有这个资源.
 **/
LIBCOM_API epicsMutexLockStatus epicsStdCall epicsMutexTryLock(
    epicsMutexId id);

/**简述:显示这个信号量的信息
 *
 * 注意:结果是依赖架构的。
 *
 * 参数:1) id 互斥锁标识符
 *       2) level 所需要报告的信息级别
 **/
LIBCOM_API void epicsStdCall epicsMutexShow(
    epicsMutexId id,unsigned  int level);

/**简述:显示所有epicsMutex信号量的信息 .
 *
 * 注意:结果是依赖架构的 
 *
 * 参数: 1) onlyLocked 非0只显示被锁的信号量 
 *        2)level 所需报告的信息等级
 **/
LIBCOM_API void epicsStdCall epicsMutexShowAll(
    int onlyLocked,unsigned  int level);

/**
 *   私有部分:以下是取决OS实现的接口并且不应该直接被用户代码调用
 */
struct epicsMutexOSD * epicsMutexOsdCreate(void);
void epicsMutexOsdDestroy(struct epicsMutexOSD *);
void epicsMutexOsdUnlock(struct epicsMutexOSD *);
epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *);
epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *);
void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned  int level);
#ifdef EPICS_PRIVATE_API
void epicsMutexOsdShowAll(void);
#endif

#ifdef __cplusplus
}
#endif

#include "osdMutex.h"

#endif /* epicsMutexh */

3) 线程函数的头文件epicsThread.h

/**
 * 文件: epicsThread.h
 *
 * 简述:对一个线程的C++ 和 C 描述。
 * epicsThread API是用于多线程编程某种程度的最小接口。
 * 在系统必须支持多线程环境的限制下,在非常多的系统上可以实现它。
 * 提供了一个POSIX pthread。
 * 接口提供了以下线程功能,注意,有以下限制:
 * - 生命周期: 作为对epicsThreadCreate的调用结果,线程开始存在。
 * 当此线程函数返回时,它终结。在它释放完它使用的所有资源前,它不应该返回。
 * 如果预计一个线程作为其生命周期自然部分而结束,则此线程必须返回。
 * - epicsThreadOnce: 这提供具有一个初始化函数的功能,确保它确切地只被调用一次。  
 * - main: 如果一个主例程完成了其工作,但想要让其它线程继续运行,它可以调用epicsThreadExitMain,
 * 这个程序必须是main中最后的语句。
 * - Priorities: 范围在0和99之间,更大数值表示更高优先级。为iocCore特定线程定义了很多常数。
 * 下层实现可能把0到99范围缩小到一个更小的范围;甚至单一优先级。用户代码不应该依赖多线程优先级
 * 的存在来保证正确的行为。
 * - Stack Size: epicsThreadCreate接受一个栈大小的参数。三个通用尺寸是可用的:small, medium和large。
 * 可移植代码应该总是使用这些通用尺寸之一。某些实现可能忽略栈尺寸的请求而是使用一个系统默认。
 * 提供大量栈尺寸的虚拟内存系统预计使用系统默认值。
 * - epicsThreadId: 每个epicsThread有一个由epicsThreadCreate返回的Id,并且只要那个线程仍然存在,
 * 其有效。一个0值总是表示没有线程。如果在线程已经终结后,使用其threadId,结果是未定义的
 * (但将通常导致不好的事情发生)。因而,照管其它线程的代码必须注意线程终结。
 */

#ifndef epicsThreadh
#define epicsThreadh

#include <stddef.h>

#include "libComAPI.h"
#include "compilerDependencies.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*EPICSTHREADFUNC)(void *parm);

/**
 * 
 * 一些指定名称的线程优先级
 */
#define epicsThreadPriorityMax          99
#define epicsThreadPriorityMin           0

/* 一些通用值 */
#define epicsThreadPriorityLow          10
#define epicsThreadPriorityMedium       50
#define epicsThreadPriorityHigh         90

/* 一些iocCore特定值 */
#define epicsThreadPriorityCAServerLow  20
#define epicsThreadPriorityCAServerHigh 40
#define epicsThreadPriorityScanLow      60
#define epicsThreadPriorityScanHigh     70
#define epicsThreadPriorityIocsh        91
#define epicsThreadPriorityBaseMax      91

/** 
 * 用于每个stackSizeClass的栈尺寸是实现和CPU相关的
 */
typedef enum {
    epicsThreadStackSmall, epicsThreadStackMedium, epicsThreadStackBig
} epicsThreadStackSizeClass;

typedef enum {
    epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess
} epicsThreadBooleanStatus;

/**
 * 获取能够被传递给epicsThreadCreate()的栈大小值
 * 参数:size: epicsThreadStackSmall, epicsThreadStackMedium 或 epicsThreadStackBig值之一。
 **/
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(
    epicsThreadStackSizeClass size);

/** (epicsThreadId)0 确保是一个无效线程id */
typedef struct epicsThreadOSD *epicsThreadId;

typedef epicsThreadId epicsThreadOnceId;
#define EPICS_THREAD_ONCE_INIT 0

/** 执行一次性初始化
 *
 * 如果提供的函数还未运行,并且未在某个其它线程中正在运行,运行它。
 * 对于每个唯一的epicsThreadOnceId,epicsThreadOnce确保
 * -# myInitFunc将只被调用一次。 
 * -# 在使用相同epicsThreadOnceId的任何其它epicsThreadOnce调用返回前,myInitFunc将返回。
 *
 * static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
 * static void myInitFunc(void *arg) { ... }
 * static void some Function(void) {
 *     epicsThreadOnce(&onceId, &myInitFunc, NULL);
 * }
 */
LIBCOM_API void epicsStdCall epicsThreadOnce(
    epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);

/**
 * 当实时调度有效时,尝试保留实时性能的任何post-init操作。
 * 对于POSIX目标,这锁定进程到RAM,防止交换相关的VM故障。
 **/
LIBCOM_API void epicsThreadRealtimeLock(void);

/**
 * 如果主程序结束时,但想要让其它线程运行,它可以调用这个例程。
 * 这应该是main中除了末尾返回外最后的调用。在大多数系统上,epicsThreadExitMain从不返回。
 * 这必须只被主线程调用。
 *
 * 弃用: 由于缺乏使用而弃用。请报告任何用法。
 * 推荐替换:loop + epicsThreadSleep(),epicsEventMustWait()或类似。
 **/
LIBCOM_API void epicsStdCall epicsThreadExitMain(void) EPICS_DEPRECATED;

/** 与epicsThreadCreateOpt()一起使用 */
typedef struct epicsThreadOpts {
    /** OSI范围中线程优先级(cf. epicsThreadPriority*) */
    unsigned int priority;
    /** 
     * Thread 栈尺寸, 要么对应这个架构的字节尺寸或者一个枚举epicsThreadStackSizeClass值
     */
    unsigned int stackSize;
    /**  线程是否应该是可联合的?(默认(0)是不可联合的)
     * 如果joinable=1, 则epicsThreadMustJoin()必须被调用用于清理线程资源
     */
    unsigned int joinable;
} epicsThreadOpts;

/** epicsThreadOpts的默认初始值。
 * 程序应该总是使用这个宏来初始化一个epicsThreadOpts结构体。
 * 在将来可能添加其它字段,并且字段顺序可能也变化,如果不遵守这些规则,
 * 认为以上定义的代码可能出错。
 */
#define EPICS_THREAD_OPTS_INIT { \
    epicsThreadPriorityLow, epicsThreadStackMedium, 0}

/** 简述:分配并且启动一个新的OS线程。
 * 参数: 1) name :一个描述这个线程的名称。在各类日志和错误消息中出现
 *        2) funptr:线程主函数
 *        3) parm :被传递给线程主函数.
 *        4) opts :这个新线程的修饰符,或者NULL来使用目标特定的默认值
 * 返回:出错返回NULL
 */
LIBCOM_API epicsThreadId epicsThreadCreateOpt (
    const char * name,
    EPICSTHREADFUNC funptr, void * parm,
    const epicsThreadOpts *opts );
    
/** 用于创建一个非联合线程的epicsThreadCreateOpt()的缩写*/
LIBCOM_API epicsThreadId epicsStdCall epicsThreadCreate (
    const char * name, unsigned int priority, unsigned int stackSize,
    EPICSTHREADFUNC funptr,void * parm );
    
/** 用于创建一个非联合线程的epicsThreadCreateOpt()的缩写.
 *  出错时,调用cantProceed()
 */
LIBCOM_API epicsThreadId epicsStdCall epicsThreadMustCreate (
    const char * name, unsigned int priority, unsigned int stackSize,
    EPICSTHREADFUNC funptr,void * parm );

/* 在vxWorks < 6.9上,这在osdThread.h中得到未定义 */
#define EPICS_THREAD_CAN_JOIN
/** 
 * 等待一个可联合线程退出(从其主程序返回)
 */
LIBCOM_API void epicsThreadMustJoin(epicsThreadId id);

/** 在epicsThreadResume()前,阻塞当前线程 */
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void);

/** 继续一个用epicsThreadSuspendSelf()挂起的线程 */
LIBCOM_API void epicsStdCall epicsThreadResume(epicsThreadId id);

/** 返回线程OSI优先级 */
LIBCOM_API unsigned int epicsStdCall epicsThreadGetPriority(epicsThreadId id);

/** 返回线程OSI优先级 */
LIBCOM_API unsigned int epicsStdCall epicsThreadGetPrioritySelf(void);

/** 更改目标线程OSI优先级 */
LIBCOM_API void epicsStdCall epicsThreadSetPriority(epicsThreadId id,unsigned int priority);

/** Lookup the next usage OSI priority such that priority > *pPriorityJustBelow
 *  if this is possible with the current target configuration and privlages.
 */
LIBCOM_API epicsThreadBooleanStatus epicsStdCall
    epicsThreadHighestPriorityLevelBelow (unsigned int priority, unsigned *pPriorityJustBelow);
    
/** Lookup the next usage OSI priority such that priority < *pPriorityJustBelow
 *  if this is possible with the current target configuration and privlages.
 */
LIBCOM_API epicsThreadBooleanStatus epicsStdCall
    epicsThreadLowestPriorityLevelAbove (unsigned int priority, unsigned *pPriorityJustAbove);
    
/** 测试两个线程IDs是否实际指向相同的OS线程 */
LIBCOM_API int epicsStdCall epicsThreadIsEqual(epicsThreadId id1, epicsThreadId id2);

/** 
 * 如何以及为什么可以挂起一个线程是取决实现的。一个调用epicsThreadSelf()的线程
 * 应该导致这个例程为此线程返回true,但可以由于其它原因挂起一个线程。
 **/
LIBCOM_API int epicsStdCall epicsThreadIsSuspended(epicsThreadId id);

/** 简述:阻塞调用线程至少指定的时间
 *  参数:要等待的秒数时间。  Values <=0 阻塞最短可能的时间
 */
LIBCOM_API void epicsStdCall epicsThreadSleep(double seconds);

/** 简述: 查询一个接近OS定时器/调度器分辨率的值
 *  返回:一个秒为单位的值 >= 0 
 *  警告:在除vxWorks和RTEMS外的目标机上,quantum值经常没有意义。
    在可移植代码中不鼓励使用这个函数
  */
LIBCOM_API double epicsStdCall epicsThreadSleepQuantum(void);

/** Find an epicsThreadId associated with the current thread.查找与当前线程相关联的epicsThreadId.
 * 对于非EPICS线程,可能分配一个新epic是ThreadId.
 */
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void);

/** 尝试通过名称查找一个线程的第一个实例。
 * 返回: 一个epicsThreadId, 或者如果没有这样的线程正在运行,返回NULL
 * 注意: 一个非NULL ID可能仍然是无效的,如果这个调用与线程退出竞争。
 * 警告: 这个函数的安全使用需要外部知道这个线程将不返回。
 */
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetId(const char *name);

/** 
 * 返回一个近似这个目标机可以并行运行线程数目的值。这个值是建议的。
 *  返回:>=1
 */
LIBCOM_API int epicsThreadGetCPUs(void);

/** 返回当前线程的名称。
 *
 * 返回:从不为NULL.  存储区终生绑定epicsThreadId
 * 这是传递给epicsThread*Create*()的字符串的副本,或者对于非EPICS线程的任意唯一字符串
 */
LIBCOM_API const char * epicsStdCall epicsThreadGetNameSelf(void);

/** Copy out the thread name into the provided buffer.
 * 复制出线程名到提供的缓存中。
 * 确保是null结尾的。
 * size是要保存名称的缓存中字节数目(包括终止符)
 * 失败导致在name中存储一个空字符串。
 */
LIBCOM_API void epicsStdCall epicsThreadGetName(
    epicsThreadId id, char *name, size_t size);

/** 一个线程能够阻塞吗?这能够被支持代码调用,它不知道自己在一个不应该阻塞的线程中被调用。
 * 例如, errlog系统调用这个函数来决定应该何时在console上显示消息。  
 **/
LIBCOM_API int epicsStdCall epicsThreadIsOkToBlock(void);

/** 当一个线程被调用时,默认是不允许它阻塞。可以调用这个方法来更改这个状态。
 * 例如,iocsh调用这个函数来指定它是可以阻塞。
 **/
LIBCOM_API void epicsStdCall epicsThreadSetOkToBlock(int isOkToBlock);

/** 向标准输出有关所有正在运行的EPICS线程的信息。
    参数: level 0打印最少输出。更高值打印更详细
 */
LIBCOM_API void epicsStdCall epicsThreadShowAll(unsigned int level);

/** 打印单个EPICS线程的信息*/
LIBCOM_API void epicsStdCall epicsThreadShow(
    epicsThreadId id,unsigned int level);

/** 当线程启动是被调用的hooks,每个线程map函数被调用一次。
 **/
typedef void (*EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id);

/**注册一个例程,在线程函数开始运行前,被每个新线程调用。
 * Hook例程经常用epicsAtThreadExit()注册一个线程退出函数来释放它们已经分配的线程专用的资源。
 */
LIBCOM_API int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook);

/**
 * 在线程创建时,从hooks列表移除例程。
 **/
LIBCOM_API int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook);

/**
 * 打印当前hook函数指针的列表
 **/
LIBCOM_API void epicsThreadHooksShow(void);

/**
 * 为每个已经线程调用func一次。
 **/
LIBCOM_API void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func);

/** 线程本地存储区 */
typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId;
/** 分配一个新线程本地变量。
 * 对于每个线程,这个变量初始将保存NULL。
 */
LIBCOM_API epicsThreadPrivateId epicsStdCall epicsThreadPrivateCreate(void);
/** 释放一个线程本地变量 */
LIBCOM_API void epicsStdCall epicsThreadPrivateDelete(epicsThreadPrivateId id);
/** 更新线程本地变量 */
LIBCOM_API void epicsStdCall epicsThreadPrivateSet(epicsThreadPrivateId,void *);
/**  获取一个线程本地变量的当前值 */
LIBCOM_API void * epicsStdCall epicsThreadPrivateGet(epicsThreadPrivateId);

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

#include "epicsEvent.h"
#include "epicsMutex.h"

//与类epicsThread一起使用的接口
class LIBCOM_API epicsThreadRunable {
public:
    virtual ~epicsThreadRunable () = 0;
    //线程主函数。
    // C++异常,它从这个方法传递将被捕获并且一个警告被打印
    //不采取其它操作
    virtual void run () = 0;
    //!可选:通过epicsThread::show()被调用
    virtual void show ( unsigned int level ) const;
};

extern "C" void epicsThreadCallEntryPoint ( void * );

/** 简述:一个OS线程
 *
 * 对epicsThread* C API的封装
 *
 * 线程必须被start()启动.
 */
class LIBCOM_API epicsThread {
public:
    /** 用提供的信息创建一个新线程
     *
     * epicsThreadOpts
     * 注意:线程必须被start()启动.
     * 出错抛出epicsThread::unableToCreateThread
     */
    epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize,
        unsigned int priority=epicsThreadPriorityLow );
    ~epicsThread () throw ();
    //实际上启动线程
    void start () throw ();
    //等待线程epicsRunnable::run()返回.
    void exitWait () throw ();
    //等待线程epicsRunnable::run()返回.
    //参数: delay 最多等待这么多秒数
    //返回:如果run()返回,true。超时,false.
    bool exitWait ( const double delay ) throw ();
    
    //! 抛出一个特殊的exitException,其将被捕获和忽略。
    //! 注意:这个exitException不是派生于std::exception
    static void exit ();
    //! cf. epicsThreadResume()
    void resume () throw ();
    //! cf. epicsThreadGetName();
    void getName ( char * name, size_t size ) const throw ();
    //! cf. epicsThreadGetIdSelf()()
    epicsThreadId getId () const throw ();
    //! cf. epicsThreadGetPriority()
    unsigned int getPriority () const throw ();
    //! cf. epicsThreadSetPriority()
    void setPriority ( unsigned int ) throw ();
    bool priorityIsEqual ( const epicsThread & ) const throw ();
    bool isSuspended () const throw ();
    //! 返回: 通过这个线程的epicsRunnable::run()调用,true
    bool isCurrentThread () const throw ();
    bool operator == ( const epicsThread & ) const throw ();
    //! 输出有感这个线程的信息到标准输出
    void show ( unsigned level ) const throw ();

    /* 这些操作对当前线程 */
    static void suspendSelf () throw ();
    static void sleep (double seconds) throw ();
    static const char * getNameSelf () throw ();
    static bool isOkToBlock () throw ();
    static void setOkToBlock ( bool isOkToBlock ) throw ();

    /* 异常 */
    class unableToCreateThread;
private:
    epicsThreadRunable & runable;
    epicsThreadId id;
    epicsMutex mutex;
    epicsEvent event;
    epicsEvent exitEvent;
    bool * pThreadDestroyed;
    bool begin;
    bool cancel;
    bool terminated;
    bool joined;

    bool beginWait () throw ();
    epicsThread ( const epicsThread & );
    epicsThread & operator = ( const epicsThread & );
    friend void epicsThreadCallEntryPoint ( void * );
    void printLastChanceExceptionMessage (
        const char * pExceptionTypeName,
        const char * pExceptionContext );
    /* 异常 */
    class exitException {};
};

class LIBCOM_API epicsThreadPrivateBase {
public:
    class unableToCreateThreadPrivate {}; /* exception */
protected:
    static void throwUnableToCreateThreadPrivate ();
};

template < class T >
class epicsThreadPrivate :
    private epicsThreadPrivateBase {
public:
    epicsThreadPrivate ();
    ~epicsThreadPrivate () throw ();
    T * get () const throw ();
    void set (T *) throw ();
private:
    epicsThreadPrivateId id;
};

#endif /* __cplusplus */

#include "osdThread.h"

#ifdef __cplusplus

template <class T>
inline epicsThreadPrivate<T>::epicsThreadPrivate ()
{
    this->id = epicsThreadPrivateCreate ();
    if ( this->id == 0 ) {
        epicsThreadPrivateBase::throwUnableToCreateThreadPrivate ();
    }
}

template <class T>
inline epicsThreadPrivate<T>::~epicsThreadPrivate () throw ()
{
    epicsThreadPrivateDelete ( this->id );
}

template <class T>
inline T *epicsThreadPrivate<T>::get () const throw ()
{
    return static_cast<T *> ( epicsThreadPrivateGet (this->id) );
}

template <class T>
inline void epicsThreadPrivate<T>::set (T *pIn) throw ()
{
    epicsThreadPrivateSet ( this->id, static_cast<void *> (pIn) );
}

#endif /* ifdef __cplusplus */

#endif /* epicsThreadh */

4)  测试代码test.c:

/*
   在本程序中,从EPICS获取更多信息并且更加结构化一点存储数据。
   为此目的,我们定义了一个"PV"结构体以及一些操作这个结构体的宏。
   你应该熟悉宏编程,用线程函数创建一个新线程。
*/

#include <stdio.h>
#include <stdlib.h>

/* 包含EPICS头文件 */
#define epicsAlarmGLOBAL
#include "epicsEvent.h"
#include "epicsMutex.h"
#include "epicsThread.h"

typedef struct Data{
        int ivalue;
        float fvalue;
}Data;

void threadfunc(void * ptr)
{
        epicsMutexId id = (epicsMutexId)ptr;
        epicsMutexLock(id);

        printf("In the trheadfunc:\n");
        epicsThreadSleep(5.0);

        epicsMutexUnlock(id);
        printf("exit trheadfunc\n");
}

int main()
{

        unsigned int size = epicsThreadGetStackSize(epicsThreadStackMedium);
        printf("size: %d\n", size);

        unsigned int priority = epicsThreadGetPrioritySelf();
        printf("priority: %d\n", priority);

        // sleep
        epicsThreadSleep(5.0);

        int cpus = epicsThreadGetCPUs();
        printf("CPUs: %d\n", cpus);

        const char * name = epicsThreadGetNameSelf();
        printf("Thread Name: %s\n", name);

        int isBlock = epicsThreadIsOkToBlock();
        printf("Block OK: %d\n",isBlock);

        int i;
        for (i = 0;i < 5; i++){
                epicsThreadShowAll(i);
                printf("\n");
        }

        epicsThreadHooksShow();

        epicsThreadPrivateId id =  epicsThreadPrivateCreate();

        Data * pdata = (Data *)calloc(sizeof(Data), 1);
        pdata->ivalue = 10;
        pdata->fvalue = 3.14;

        epicsThreadPrivateSet(id, (void *)pdata);

        Data * ptmp = NULL;

        epicsThreadSleep(1.0);

        ptmp = (Data *)epicsThreadPrivateGet(id);

        printf("ivalue = %d, fvalue = %f\n", ptmp->ivalue, ptmp->fvalue);

        free(ptmp);

        epicsThreadPrivateDelete(id);


        epicsMutexId mutexid = epicsMutexMustCreate();
        printf("create mutex successfully\n");


         epicsMutexLock(mutexid);

         epicsThreadMustCreate("thread1", 0, 0, threadfunc, (void *)mutexid);
         printf("sleep in main\n");
         epicsThreadSleep(5.0);
         printf("sleep over\n");
         epicsMutexUnlock(mutexid);
         epicsThreadSleep(1.0);

         epicsMutexLock(mutexid);
         epicsThreadSleep(1.0);
         epicsMutexUnlock(mutexid);

        epicsMutexDestroy(mutexid);
        return 0;
}

5) 编译以上测试程序并进行测试结果:

orangepi@orangepi4-lts:~/host_program/host/hostApp$ O.linux-aarch64/test
size: 1048576
priority: 0
CPUs: 6
Thread Name: _main_
Block OK: 1
            NAME       EPICS ID   LWP ID   OSIPRI  OSSPRI  STATE
          _main_ 0xaaaac55431d0   879288      0       0       OK

            NAME       EPICS ID   LWP ID   OSIPRI  OSSPRI  STATE
          _main_ 0xaaaac55431d0   879288      0       0       OK

            NAME       EPICS ID   LWP ID   OSIPRI  OSSPRI  STATE
          _main_ 0xaaaac55431d0   879288      0       0       OK

            NAME       EPICS ID   LWP ID   OSIPRI  OSSPRI  STATE
          _main_ 0xaaaac55431d0   879288      0       0       OK

            NAME       EPICS ID   LWP ID   OSIPRI  OSSPRI  STATE
          _main_ 0xaaaac55431d0   879288      0       0       OK

  0xffff9fdb59c0
ivalue = 10, fvalue = 3.140000
create mutex successfully
sleep in main
sleep over
In the trheadfunc:
exit trheadfunc

文章来源地址https://www.toymoban.com/news/detail-625649.html

到了这里,关于EPICS多线程编程的库文件解析及程序测试实例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C标准库文件&常用函数

    编号 头文件 C标准版本 介绍 1 assert.h C89/C90 条件编译宏,将参数与零比较 2 ctype.h C89/C90 用来确定包含于字符数据中的类型的函数 3 errno.h C89/C90 报告错误条件的宏 4 float.h C89/C90 浮点数类型的极限 5 limits.h C89/C90 基本类型的大小 6 locale.h C89/C90 本地化工具 7 math.h C89/C90 常用数据函

    2024年02月12日
    浏览(47)
  • C++有哪些常用的库文件

    常用库文件: iostream:  输入输出流库,包含cin、cout、cerr等标准输入输出对象。 string : 字符串库,包含字符串类型std::string及相关操作函数。 vector : 动态数组库,包含vector类型及相关操作函数。 map:  字典库,包含map、multimap、set、multiset等关联容器类型及相关操作函数。 algorithm

    2024年02月02日
    浏览(44)
  • 7-LINUX--库文件的生成与使用

    库是一组预先编译好的方法的集合。Linux系统存储的库的位置一般在:/lib 和 /usr/lib。 在 64 位的系统上有些库也可能被存储在/usr/lib64 下。库的头文件一般会被存储在 /usr/include 下或其子目录下。 库有两种,一种是静态库,其命令规则为 libxxx.a,一种是共享库,其命令规则为

    2024年03月16日
    浏览(47)
  • makefile 编译动态链接库使用(.so库文件)

    动态链接库:不会把代码编译到二进制文件中,而是在运行时才去加载, 好处是程序可以和库文件分离,可以分别发版,然后库文件可以被多处共享 动态链接库 动态:动态加载 链接:二进制文件和库文件分离。 库 库文件 .so 新建一个文件TestSo 编译一下 main.cpp 写好之后我们

    2024年01月23日
    浏览(44)
  • 现代CMake高级教程 - 第 3 章:链接库文件

    双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记 main.cpp 调用 mylib.cpp 里的 say_hello 函数 1. 直接链接到一起编译 2. mylib 作为一个静态库 编译: 生成了 libmylib.a: 3. mylib 作为一个动态库 编译: 4. mylib 作为一个对象库 对象库类似于静态库,但不生成 .a 文件,只由 CMake

    2024年02月02日
    浏览(41)
  • 通过 CMake 制作库文件 静态库 和 动态库

    CMake Calc 项目 在这里有 add.c,div.c,mult.c,sub.c,main.c,head.h 二、生成静态库  CMakeLists.txt  CMakeLists.txt   (也可以写成这样) 执行命令:  三、生成动态库 CMakeLists.txt    CMakeLists.txt   (也可以写成这样) 

    2024年01月16日
    浏览(50)
  • 第十二部分 使用 make 更新函数库文件

    目录 前言  一、函数库文件的成员 二、函数库成员的隐含规则 三、函数库文件的后缀规则 四、注意事项         函数库文件也就是对 Object 文件(程序编译的中间文件)的打包文件。在 Unix 下,一 般是由命令\\\"ar\\\"来完成打包工作。         一个函数库文件由多个文件

    2024年01月20日
    浏览(48)
  • openssl缺少libssl.so.1.1库文件

    这是由于openssl库的位置不正确造成的,一般出现在openssl源码升级场景下。 源码编译完成后,需要对libssl.so.1.1做一个软连接,从openssl源码安装目录/usr/local/openssl/lib/下面将编译好的libssl.so.1.1进行软连接。

    2024年01月21日
    浏览(46)
  • 【cmake学习】cmake 引入第三方库(头文件目录、库目录、库文件)

    程序的编写需要用到头文件,程序的编译需要lib文件,程序的运行需要dll文件,因此cmake引入第三方库其实就是将include目录、lib目录、bin目录引入工程。         目录 1、find_package(批量引入库文件和头文件) 2、include_directories(引入头文件目录) 3、link_directories(引入库

    2024年02月09日
    浏览(56)
  • Python3: 扫描库文件并获取版本号信息

    在 C/C++ 开发中使用了第三方库,具体说是 .a , .lib , .dll 等文件,想通过 Python 查询出这些文件中的版本号信息。 有人可能好奇,这里简单消除可能得疑虑: 为什么不用源代码,而用库? 因为库文件提供了良好的 隔离性 ,避免了繁杂的编译选项指定, 避免了潜在的不小心改

    2024年02月05日
    浏览(83)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包