📄 thread.h
字号:
* * @author David Sugar <dyfet@ostel.com> * @short Thread protected integer counter. */class __EXPORT MutexCounter : public Mutex{private: volatile int counter;public: /** * Create and optionally name a mutex protected counter. * * @param id name for mutex counter, optional for deadlock testing. */ MutexCounter(const char *id = NULL); /** * Create and optionally name a mutex protected counter with * an initial value. * * @param initial value of counter. * @param id name of counter, optional for deadlock testing. */ MutexCounter(int initial, const char *id = NULL); friend __EXPORT int operator++(MutexCounter &mc); friend __EXPORT int operator--(MutexCounter &mc);};/** * The AtomicCounter class offers thread-safe manipulation of an integer * counter. These are commonly used for building thread-safe "reference" * counters for C++ classes. The AtomicCounter depends on the platforms * support for "atomic" integer operations, and can alternately substitute * a "mutex" if no atomic support exists. * * @author Sean Cavanaugh <sean@dimensionalrift.com> * @short atomic counter operation. */class __EXPORT AtomicCounter{#ifndef CCXX_USE_WIN32_ATOMICprivate:#if defined(HAVE_ATOMIC_AIX) volatile int counter;#elif defined(HAVE_GCC_BITS_ATOMIC) volatile _Atomic_word counter;#elif defined(HAVE_GCC_CXX_BITS_ATOMIC) volatile _Atomic_word counter;// __gnu_cxx::_Atomic_word counter;#elif defined(HAVE_ATOMIC) atomic_t atomic;#else volatile int counter; pthread_mutex_t _mutex;#endifpublic: /** * Initialize an atomic counter to 0. */ AtomicCounter(); /** * Initialize an atomic counter to a known value. * * @param value initial value. */ AtomicCounter(int value); ~AtomicCounter(); int operator++(void); int operator--(void); int operator+=(int change); int operator-=(int change); int operator+(int change); int operator-(int change); int operator=(int value); bool operator!(void); operator int();#elseprivate: long atomic;public: inline AtomicCounter() {atomic = 0;}; inline AtomicCounter(int value) {atomic = value;}; inline int operator++(void) {return InterlockedIncrement(&atomic);}; inline int operator--(void) {return InterlockedDecrement(&atomic);}; int operator+=(int change); int operator-=(int change); inline int operator+(int change) {return atomic + change;}; inline int operator-(int change) {return atomic - change;}; inline int operator=(int value) {return InterlockedExchange(&atomic, value);}; inline bool operator!(void) {return (atomic == 0) ? true : false;}; inline operator int() {return atomic;};#endif};#ifndef WIN32/** * A conditional variable synchcronization object for one to one and * one to many signal and control events between processes. * Conditional variables may wait for and receive signals to notify * when to resume or perform operations. Multiple waiting threads may * be woken with a broadcast signal. * * @warning While this class inherits from Mutex, the methods of the * class Conditional just handle the system conditional variable, so * the user is responsible for calling enterMutex and leaveMutex so as * to avoid race conditions. Another thing to note is that if you have * several threads waiting on one condition, not uncommon in thread * pools, each thread must take care to manually unlock the mutex if * cancellation occurs. Otherwise the first thread cancelled will * deadlock the rest of the thread. * * @author David Sugar * @short conditional. * @todo implement in win32 */class __EXPORT Conditional {private: pthread_cond_t _cond; pthread_mutex_t _mutex;public: /** * Create an instance of a conditional. * * @param id name of conditional, optional for deadlock testing. */ Conditional(const char *id = NULL); /** * Destroy the conditional. */ virtual ~Conditional(); /** * Signal a conditional object and a waiting threads. * * @param broadcast this signal to all waiting threads if true. */ void signal(bool broadcast); /** * Wait to be signaled from another thread. * * @param timer time period to wait. * @param locked flag if already locked the mutex. */ bool wait(timeout_t timer = 0, bool locked = false); /** * Locks the conditional's mutex for this thread. Remember * that Conditional's mutex is NOT a recursive mutex! * * @see #leaveMutex */ void enterMutex(void); /** * In the future we will use lock in place of enterMutex since * the conditional composite is not a recursive mutex, and hence * using enterMutex may cause confusion in expectation with the * behavior of the Mutex class. * * @see #enterMutex */ inline void lock(void) {enterMutex();}; /** * Tries to lock the conditional for the current thread. * Behaves like #enterMutex , except that it doesn't block the * calling thread. * * @return true if locking the mutex was succesful otherwise false * * @see enterMutex * @see leaveMutex */ bool tryEnterMutex(void); inline bool test(void) {return tryEnterMutex();}; /** * Leaving a mutex frees that mutex for use by another thread. * * @see #enterMutex */ void leaveMutex(void); inline void unlock(void) {return leaveMutex();};};#endif/** * A semaphore is generally used as a synchronization object between multiple * threads or to protect a limited and finite resource such as a memory or * thread pool. The semaphore has a counter which only permits access by * one or more threads when the value of the semaphore is non-zero. Each * access reduces the current value of the semaphore by 1. One or more * threads can wait on a semaphore until it is no longer 0, and hence the * semaphore can be used as a simple thread synchronization object to enable * one thread to pause others until the thread is ready or has provided data * for them. Semaphores are typically used as a * counter for protecting or limiting concurrent access to a given * resource, such as to permitting at most "x" number of threads to use * resource "y", for example. * * @author David Sugar <dyfet@ostel.com> * @short Semaphore counter for thread synchronization. */class __EXPORT Semaphore{private:#ifndef WIN32 unsigned _count, _waiters; pthread_mutex_t _mutex; pthread_cond_t _cond;#else HANDLE semObject;#endif // !WIN32public: /** * The initial value of the semaphore can be specified. An initial * value is often used When used to lock a finite resource or to * specify the maximum number of thread instances that can access a * specified resource. * * @param resource specify initial resource count or 0 default. */ Semaphore(unsigned resource = 0); /** * Destroying a semaphore also removes any system resources * associated with it. If a semaphore has threads currently waiting * on it, those threads will all continue when a semaphore is * destroyed. */ virtual ~Semaphore(); /** * Wait is used to keep a thread held until the semaphore counter * is greater than 0. If the current thread is held, then another * thread must increment the semaphore. Once the thread is accepted, * the semaphore is automatically decremented, and the thread * continues execution. * * The pthread semaphore object does not support a timed "wait", and * hence to maintain consistancy, neither the posix nor win32 source * trees support "timed" semaphore objects. * * @return false if timed out * @param timeout period in milliseconds to wait * @see #post */ bool wait(timeout_t timeout = 0); /** * Posting to a semaphore increments its current value and releases * the first thread waiting for the semaphore if it is currently at * 0. Interestingly, there is no support to increment a semaphore by * any value greater than 1 to release multiple waiting threads in * either pthread or the win32 API. Hence, if one wants to release * a semaphore to enable multiple threads to execute, one must perform * multiple post operations. * * @see #wait */ void post(void); // FIXME: how implement getValue for posix compatibility ? // not portable...#if 0 /** * Get the current value of a semaphore. * * @return current value. */ int getValue(void);#endif};/** * The SemaphoreLock class is used to protect a section of code through * a semaphore so that only x instances of the member function may * execute concurrently. * * A common use is * * void func_to_protect() * { * SemaphoreLock lock(semaphore); * ... operation ... * } * * NOTE: do not declare variable as "SemaohoreLock (semaphore)", the * mutex will be released at statement end. * * @author David Sugar <dyfet@gnu.org> * @short Semaphore automatic locker for protected access. */class __EXPORT SemaphoreLock{private: Semaphore& sem;public: /** * Wait for the semaphore */ SemaphoreLock( Semaphore& _sem ) : sem( _sem ) { sem.wait(); } /** * Post the semaphore automatically */ // this should be not-virtual ~SemaphoreLock() { sem.post(); }};/** * The Event class implements a feature originally found in the WIN32 API; * event notification. A target thread waits on a resetable Event, and one * or more other threads can then signal the waiting thread to resume * execution. A timeout can be used to specify a wait duration in * milliseconds. The Event class must be reset before it can be used again * as a trigger. These event objects * use a trigger/reset mechanism and are related to low level conditional * variables. * * @author: David Sugar <dyfet@ostel.com> * @short Thread synchornization on event notification. */class __EXPORT Event{private:#ifndef WIN32 pthread_mutex_t _mutex; pthread_cond_t _cond; bool _signaled; int _count;#else HANDLE cond;#endifpublic: Event(); virtual ~Event(); /** * Once signaled, the Event class must be "reset" before responding * to a new signal. * * @see #signal */ void reset(void); /** * Signal the event for the waiting thread. */ void signal(void); /** * Wait either for the event to be signaled by another thread or * for the specified timeout duration. * * @see #signal * @return true if signaled, false if timed out. * @param timer timeout in milliseconds to wait for a signal. */ bool wait(timeout_t timer); bool wait(void);};/** * Every thread of execution in an application is created by * instantiating an object of a class derived from the Thread * class. Classes derived from Thread must implement the run() method, * which specifies the code of the thread. The base Thread class * supports encapsulation of the generic threading methods implemented * on various target operating systems. This includes the ability to * start and stop threads in a synchronized and controllable manner, * the ability to specify thread execution priority, and thread * specific "system call" wrappers, such as for sleep and yield. A * thread exception is thrown if the thread cannot be created. * Threading was the first part of Common C++ I wrote, back when it * was still the APE library. My goal for Common C++ threading has * been to make threading as natural and easy to use in C++ * application development as threading is in Java. With this said, * one does not need to use threading at all to take advantage of * Common C++. However, all Common C++ classes are designed at least * to be thread-aware/thread-safe as appropriate and necessary. * * Common C++ threading is currently built either from the Posix "pthread" * library or using the win32 SDK. In that the Posix "pthread" draft * has gone through many revisions, and many system implementations are * only marginally compliant, and even then usually in different ways, I * wrote a large series of autoconf macros found in ost_pthread.m4 which * handle the task of identifying which pthread features and capabilities * your target platform supports. In the process I learned much about what * autoconf can and cannot do for you.. * * Currently the GNU Portable Thread library (GNU pth) is not directly * supported in Common C++. While GNU "Pth" doesn't offer direct * native threading support or benefit from SMP hardware, many of the design * advantages of threading can be gained from it's use, and the Pth pthread * "emulation" library should be usable with Common C++. In the future, * Common C++ will directly support Pth, as well as OS/2 and BeOS native * threading API's. * * Common C++ itself defines a fairly "neutral" threading model that is * not tied to any specific API such as pthread, win32, etc. This neutral * thread model is contained in a series of classes which handle threading * and synchronization and which may be used together to build reliable * threaded applications. * * Common C++ defines application specific threads as objects which are * derived from the Common C++ "Thread" base class. At minimum the "Run" * method must be implemented, and this method essentially is the "thread", * for it is executed within the execution context of the thread, and when * the Run method terminates the thread is assumed to have terminated. * * Common C++ allows one to specify the running priority of a newly created * thread relative to the "parent" thread which is the thread that is * executing when the constructor is called. Since most newer C++ * implementations do not allow one to call virtual constructors or virtual * methods from constructors, the thread must be "started" after the * constructor returns. This is done either by defining a "starting" * semaphore object that one or more newly created thread objects can wait * upon, or by invoking an explicit "start" member function. * * Threads can be "suspended" and "resumed". As this behavior is not defined * in the Posix "pthread" specification, it is often emulated through * signals. Typically SIGUSR1 will be used for this purpose in Common C++ * applications, depending in the target platform. On Linux, since threads * are indeed processes, SIGSTP and SIGCONT can be used. On solaris, the * Solaris thread library supports suspend and resume directly. * * Threads can be canceled. Not all platforms support the concept of * externally cancelable threads. On those platforms and API * implementations that do not, threads are typically canceled through the * action of a signal handler. * * As noted earlier, threads are considered running until the "Run" method * returns, or until a cancellation request is made. Common C++ threads can * control how they respond to cancellation, using setCancellation(). * Cancellation requests can be ignored, set to occur only when a * cancellation "point" has been reached in the code, or occur immediately. * Threads can also exit by returning from Run() or by invoking the Exit() * method. * * Generally it is a good practice to initialize any resources the thread may * require within the constructor of your derived thread class, and to purge * or restore any allocated resources in the destructor. In most cases, the * destructor will be executed after the thread has terminated, and hence * will execute within the context of the thread that requested a join rather * than in the context of the thread that is being terminated. Most * destructors in derived thread classes should first call Terminate() to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -