/** This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3* http://www.gnu.org/licenses/lgpl-3.0.html*/#ifndef CBTHREADPOOL_H#define CBTHREADPOOL_H#include <wx/thread.h>#include <wx/event.h>#include <vector>#include <list>#include "cbthreadedtask.h"#include "settings.h"#include "prep.h"/// A Thread Pool implementationclass DLLIMPORT cbThreadPool{public:/** cbThreadPool ctor** @param owner Event handler to receive cbEVT_THREADTASK_ENDED and cbEVT_THREADTASK_ALLDONE events* @param id Used with the events* @param concurrentThreads Number of threads in the pool. -1 means current CPU count*/cbThreadPool(wxEvtHandler *owner, int id = -1, int concurrentThreads = -1, unsigned int stackSize = 0);/// cbThreadPool dtor~cbThreadPool();/** Changes the number of threads in the pool** @param concurrentThreads New number of threads. -1 or 0 means current CPU count* @note If tasks are running, it'll delay it until they're all done.*/void SetConcurrentThreads(int concurrentThreads);/** Gets the current number of threads in the pool** @return Number of threads in the pool* @note If a call to SetConcurrentThreads hasn't been applied, it'll return the* number of threads that will be set by it when all tasks be done.*/int GetConcurrentThreads() const;/** return the pool ID */int GetId() const { return m_ID; }/** Adds a new task to the pool** @param task The task to execute* @param autodelete If true, the task will be deleted when it finishes or be aborted*/void AddTask(cbThreadedTask *task, bool autodelete = true);/** Aborts all running and pending tasks** @note Calls cbThreadedTask::Abort for all running tasks and just removes the pending ones.*/void AbortAllTasks();/** Tells if the pool has finished its job** @return true if it has nothing to do, false if it's executing tasks*/bool Done() const;/** Begin a batch process** @note EVIL: Call it if you want to add all tasks first and get none executed yet.* If you DON'T call it, tasks will be executed as you add them (in fact it's what* one would expect).* @note If when calling the BatchBegin(), the pool is ready running tasks, then the added* task still has chance to run, so be best time to call this function is Done() returns true*/void BatchBegin();/** End a batch process** @note EVIL: Call it when you have finished adding tasks and want them to execute.* BEWARE: if you call BatchBegin but DON'T call BatchEnd, the tasks WON'T execute.*/void BatchEnd();private:/// Josuttis' implementation of CountedPtr/* class for counted reference semantics* - deletes the object to which it refers when the last CountedPtr* that refers to it is destroyed*/template <typename T>class CountedPtr{private:T *ptr; // pointer to the valuelong *count; // shared number of ownerspublic:// initialize pointer with existing pointer// - requires that the pointer p is a return value of newexplicit CountedPtr(T *p = nullptr) : ptr(p), count(new long(1)) {}// copy pointer (one more owner)CountedPtr(const CountedPtr<T> &p) : ptr(p.ptr), count(p.count){++*count;}// destructor (delete value if this was the last owner)~CountedPtr(){dispose();}/// assignment (unshare old and share new value)CountedPtr<T> &operator = (const CountedPtr<T> &p){if (this != &p){dispose();ptr = p.ptr;count = p.count;++*count;}return *this;}/// access the value to which the pointer refersT &operator * () const{return *ptr;}T *operator -> () const{return ptr;}private:/** decrease the counter, and if it get 0, destroy both counter and value */void dispose(){if (--*count == 0){delete count;delete ptr;}}};/** A Worker Thread class.** These are the ones that execute the tasks.* You shouldn't worry about it since it's for "private" purposes of the Pool.*/class cbWorkerThread : public wxThread{public:/** cbWorkerThread ctor** @param pool Thread Pool this Worker Thread belongs to* @param semaphore Used to synchronize the Worker Threads, it is a reference to the CountedPtr* object*/cbWorkerThread(cbThreadPool *pool, CountedPtr<wxSemaphore> &semaphore);/// Entry point of this thread. The magic happens here.ExitCode Entry();/// Tell the thread to abort. It will also tell the task to abort (if any)void Abort();/** Tells whether we should abort or not** @return true if we should abort*/bool Aborted() const;/// Aborts the running task (if any)void AbortTask();private:/** whether is is aborted or not */bool m_abort;/** point to the pool which the thread belong to */cbThreadPool *m_pPool;/** a pointer to the wxSemaphore* it is a counted semaphore pointer shared with all the cbWorkerThread*/CountedPtr<wxSemaphore> m_semaphore;/** a pointer to the running task */cbThreadedTask *m_pTask;/** to protect the member variable accessing from multiply threads* lock the access to the m_pTask* cbWorkerThread::AbortTask() which access to m_pTask may be called from poll when thread* is running*/wxMutex m_taskMutex;};typedef std::vector<cbWorkerThread *> WorkerThreadsArray;/// All tasks are added to one of these. It'll also save the autodelete valuestruct cbThreadedTaskElement{cbThreadedTaskElement(cbThreadedTask *_task = nullptr, bool _autodelete = false): task(_task),autodelete(_autodelete){// empty}/// It'll delete the task only if it was set tovoid Delete(){if (autodelete){delete task;task = nullptr; // better safe than sorry}}cbThreadedTask *task;bool autodelete;};typedef std::list<cbThreadedTaskElement> TasksQueue;wxEvtHandler *m_pOwner; // events notification will send to this guyint m_ID; // id used to fill the ID field of the eventbool m_batching; // whether in batch mode of adding tasks// current number of concurrent threads, this is the maximum value of the m_workingThreads// this variable should always be positive, 0 and -1 is not allowed.int m_concurrentThreads;unsigned int m_stackSize; // stack size for every threads// if we cannot apply the new value of concurrent threads, keep it here, usually the time to// apply a scheduled value is when all the tasks is done.int m_concurrentThreadsSchedule;// the total threads(cbWorkerThread) are stored here, this contains all the threads either is// currently running or in idle() mode.WorkerThreadsArray m_threads;// the pending tasks (cbThreadedTaskElement), usually we have many tasks to run in the pool, but// we have limited number of threads to run those tasks, so tasks which don't have the chance to// run will be put in the queue. Once a thread finishes a task, it will fetch a new task from// this task queue.TasksQueue m_tasksQueue;// true if any task added, reset to false if all the tasks is donebool m_taskAdded;/** how many working threads are running tasks* m_workingThreads + thread in Idle = m_concurrentThreads*/int m_workingThreads;mutable wxMutex m_Mutex; // we better be safe, protect the change of member variables// used to synchronize the Worker Threads, the counted value is that how many threads are// sharing this semaphore. The semaphore's initial value is the thread number we can used to// run the tasks.// initial counted value = m_concurrentThreads// the value of semaphore = the number of threads in Idle modeCountedPtr<wxSemaphore> m_semaphore;void _SetConcurrentThreads(int concurrentThreads); // like SetConcurrentThreads, but non-thread safe// awakes all threads, so they will leave from the Idle mode to working mode// this is used when we are going to abort all the threads, there are two// cases we need to call Broadcast(), one is the destructor, the other is the user need to// change the concurrent thread numbers, so we abort all the threads, and re-create them again.void Broadcast();// awakes only a few threads, this usually happens when we add some tasks, and there are some// threads which is currently in idle mode, so we can awake these idle threads to run tasks.void AwakeNeeded();protected:friend class cbWorkerThread;/** Returns the next task in the queue to run** @return Next task to run, or a NULL task (set in .task) if none*/cbThreadedTaskElement GetNextTask();/// Mechanism for the threads to tell the Pool they're running, a thread is switch from the idle/// mode to working mode. This is triggered by semaphore released somewhere/// this function will be called in the worker thread, the thread just say: hey, I'm running now/// so increase the running thread number by onevoid WorkingThread();/** Mechanism for the threads to tell the Pool they're done and will go to idle, so we can assign* another task to this thread.* this function will be called in the worker thread, it just say: hey, I have finished one task* thus, decrease the running thread number by one, and let me go to idle mode** @return true if everything is OK, false if we should abort, this usually happens we need to* set a scheduled m_concurrentThreads value.*/bool WaitingThread();/** Called by a Worker Thread to inform a single task has finished, this will send a cbEVT_THREADTASK_ENDED event** @param thread The Worker Thread*/void TaskDone(cbWorkerThread *thread);};/* ************************************************ *//* **************** INLINE MEMBERS **************** *//* ************************************************ */inline cbThreadPool::cbThreadPool(wxEvtHandler *owner, int id, int concurrentThreads, unsigned int stackSize): m_pOwner(owner),m_ID(id),m_batching(false),m_concurrentThreads(-1),m_stackSize(stackSize),m_concurrentThreadsSchedule(0),m_taskAdded(false),m_workingThreads(0),m_semaphore(new wxSemaphore){// m_concurrentThreads will be set to a positive integer value.SetConcurrentThreads(concurrentThreads);}inline int cbThreadPool::GetConcurrentThreads() const{wxMutexLocker lock(m_Mutex);return m_concurrentThreadsSchedule ? m_concurrentThreadsSchedule : m_concurrentThreads;}inline bool cbThreadPool::Done() const{wxMutexLocker lock(m_Mutex);return m_workingThreads == 0;}inline void cbThreadPool::BatchBegin(){wxMutexLocker lock(m_Mutex);m_batching = true;}inline void cbThreadPool::Broadcast(){// if m_concurrentThreads == -1, which means the pool is not initialized yetif (m_concurrentThreads == -1)return;// let the idle(pending) worker thread to execute tasks, those worker threads are waiting for semaphorefor (std::size_t i = 0; i < static_cast<std::size_t>(m_concurrentThreads - m_workingThreads); ++i)m_semaphore->Post();}inline void cbThreadPool::AwakeNeeded(){// if m_concurrentThreads == -1, which means the pool is not initialized yetif (m_concurrentThreads == -1)return;// the thread number to awake should be less than the idle thread number and the tasks queue's sizestd::size_t awakeThreadNumber = std::min<std::size_t>(m_tasksQueue.size(),(m_concurrentThreads - m_workingThreads));for (std::size_t i = 0; i < awakeThreadNumber; ++i)m_semaphore->Post();}#endif //CBTHREADPOOL_H
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。