C++强化之路之线程池开发整体框架(二)

一.线程池开发框架

我所开发的线程池由以下几部分组成: 
1.工作中的线程。也就是线程池中的线程,主要是执行分发来的task。 
2.管理线程池的监督线程。这个线程的创建独立于线程池的创建,按照既定的管理方法进行管理线程池中的所有线程,主要任务是监听任务的到来,唤醒线程池中的空闲线程,分发任务;如果任务增多,动态的创建一批线程加入原来的线程池中,进行工作;适当的销毁线程,减少系统开销。

这个线程池的开发涉及了以下几个数据结构、设计模式和软件结构: 
1.任务队列。整个框架有两个任务队列,1)等待任务队列(以下简称wait_queue)。2)正在执行中任务队列(以下简称doing_queue)。队列采用先进先出的数据结构。当一个任务来到时,会先被push到wait_queue,监督线程会一直监督wait_queue中的元素,一旦有任务,便会pop wait_queue中的元素,再push到doing_queue中。 
2.单例设计模式。线程池的类被设计成单例模式,防止一个程序中多次创建线程池对象,出现紊乱现象,用户只能调用静态方法初始化得到线程池的对象。 
3.回调函数。回调函数的设计主要是为了能够把任务接口(也就是需要线程去执行的任务,通常是一个写好的函数方法)提前初始化注册,然后延迟调用。

下图是所用类的的大概结构图: 
这里写图片描述

程序整体结构如下: 
这里写图片描述

二.线程池开发具体实现

1.思路分析。 
线程池顾名思义就是同时有数个线程处于待执行状态,编码上的初始化的实现无非就是循环创建指定数量的线程,然后等待任务的到来唤醒空闲线程。以下是ThreadPoll的类:

class ThreadPool 
{private:pthread_t *_thread;  //线程池pthread_t *_thread_bak; //备用线程池,当任务过多会自动创建pthread_t taskqueue_thread; //管理线程int u4sequence;int wait_time;int CANCEL_SIGNAL;bool F_improve_ThrdPoll; //备用线程池创建标志Mutex *mutex;  //互斥锁CondThread *task_cond; //条件变量TaskFuncCallback callback; //声明回调函数,即线程所需要执行的函数int _num_threads;  //线程池数量//构造函数的实现为private属性,禁止用户用构造函数初始化对象。ThreadPool(int num_threads):_num_threads(num_threads),F_improve_ThrdPoll(0),wait_time(0),u4sequence(0),CANCEL_SIGNAL(0){init(); //一些变量的创建ManagerThreadInit(); //创建管理线程ThreadPoolInit(num_threads);//初始化线程池}public:LVQueue<TASK_QUEUE_T> task_wait_queue;//创建任务等待队列LVQueue<TASK_QUEUE_T> task_doing_queue;//创建任务执行队列~ThreadPool(){delete(mutex);delete(task_cond);delete(_thread);delete(_thread_bak);}//用户通过调用此静态方法得到线程池的对象(单例模式)static ThreadPool* createThreadPool(int num){   static ThreadPool *_pool = new ThreadPool(num);return _pool;}void init(){_thread = new pthread_t[_num_threads];mutex = new Mutex();task_cond = new CondThread();}API_RETURN_TYPE_T ThreadPoolInit(int num_thr);//线程池初始化,核心接口API_RETURN_TYPE_T run(); //线程执行函数API_RETURN_TYPE_T ManagerThreadInit();//管理线程初始化API_RETURN_TYPE_T managerThread();线程执行函数API_RETURN_TYPE_T wakeupThread(TaskFuncCallback p_func);//用户调用此接口唤醒线程执行任务,参数为传入的任务执行函数地址API_RETURN_TYPE_T AutoComputeOptimumThreadNum(int wait_que_num,int &_u4sequence);//一种自动计算需要增加多少线程到线程池,当任务繁多时会调到。API_RETURN_TYPE_T ThreadJoin();//所有线程阻塞API_RETURN_TYPE_T ReleaseSubThreadPool();//释放备用线程池API_RETURN_TYPE_T DestroyThreadPool();//释放线程池       
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

下面是cpp的实现:

//线程池初始化的实现
API_RETURN_TYPE_T ThreadPool::ThreadPoolInit(int num_thr)
{printf("num = %d.\n",num_thr);  if(num_thr == 0){return API_SUCCESS;}//设置创建线程的属性为DETACHED,线程被释放后,资源会被回收。pthread_attr_t attr;pthread_attr_init (&attr);pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);int i = 0;if(F_improve_ThrdPoll == 1)//备用线程池创建的标志位,初始化线程不会走这边{_thread_bak = new pthread_t[num_thr];for(;i < num_thr;i++){if(RET_OK != pthread_create(&(_thread_bak[i]), &attr, &thread_func, this)){return API_FAIL;}}return API_SUCCESS;}//create thread pool.for(;i < num_thr;i++){if(RET_OK != pthread_create(&(_thread[i]), &attr, &thread_func, this)){return API_FAIL;}}pthread_attr_destroy (&attr);return API_SUCCESS;
}
//创建的所有线程都会跑到这个线程函数,最终指向run
void  *thread_func(void *arg)
{ThreadPool *thread = (ThreadPool*)arg;thread->run();
}
//线程池核心内容
API_RETURN_TYPE_T ThreadPool::run()
{//printf("this is run  thread.\n");void *arg;while(1)//线程池内部一直在循环{printf ("thread 0x%x begin\n", pthread_self ()); this->mutex->lock();//上锁if((CANCEL_SIGNAL == 0) && (task_doing_queue.length() < _num_threads || F_improve_ThrdPoll == 1) )//以上条件第一个是备用线程释放标志,第二个是任务执行队列数量为0,第三个是备用线程创建标志(或的关系,为了满足新增线程进入wait状态),第一次这些条件都会满足{printf ("thread 0x%x is waiting\n", pthread_self ());  this->task_cond->wait(mutex);//每次创建的新线程都会阻塞到这里,执行完任务的线程也会阻塞在这里,等待唤醒的signal,虽然是阻塞在这里,但是互斥锁已经是unlock状态了,这是linux的机制。}usleep(200000);this->mutex->unlock();//解锁pthread_testcancel();//设置取消线程点pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//(1)头尾保护下面这段code,保证在执行任务的时候屏蔽外部的线程取消信号if(callback != NULL){callback(arg); //执行回调函数,此时的回调函数应该指向当前任务执行函数的地址callback = NULL;}task_doing_queue.popFront();//执行完任务,任务执行队列出队列元素一个pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//同(1)printf("wait len =%d.\n",task_wait_queue.length());printf("thread 0x%x done.length() = %d.\n",pthread_self (),task_doing_queue.length());}return API_SUCCESS;}//管理线程的初始化
API_RETURN_TYPE_T ThreadPool::ManagerThreadInit()
{//create manager threadpool thread.if(RET_OK != pthread_create(&taskqueue_thread, NULL, &thread_task_queue, this)){   return API_FAIL;}return API_SUCCESS;
}
//管理线程的执行函数
void *thread_task_queue(void *arg)
{   ThreadPool *thread = (ThreadPool*)arg;thread->managerThread();}
//管理线程的核心内容
API_RETURN_TYPE_T ThreadPool::managerThread()
{while(1){usleep(400000);printf("managerThread!.\n");this->mutex->lock();//上锁TASK_QUEUE_T task1; //初始化两个队列元素对象TASK_QUEUE_T task2;task1.sTask = TASK_DOING;if(task_wait_queue.length() != 0){//printf("len =%d.\n",task_doing_queue.length());if(task_doing_queue.length() < _num_threads)//只要任务执行队列的数目小于线程池中线程的总数,就会执行{task2 = task_wait_queue.popFront();//pop任务等待队列的元素,并得到这个元素的对象callback = task2.cTaskFunc;//获得任务的执行函数地址task_doing_queue.pushBack(task1);//将任务push到任务执行队列task_cond->signal();//发送信号,唤醒一个空闲线程printf("signal cond.\n");}}//当人任务队列的等待任务数量大于线程池线程总数时会执行if(task_wait_queue.length() >= _num_threads && F_improve_ThrdPoll == 0){//通过简单的机制计算当前是否需要另外新增线程到线程池AutoComputeOptimumThreadNum(task_wait_queue.length(),u4sequence);F_improve_ThrdPoll = 1;ThreadPoolInit(u4sequence);//如果需要新增线程,u4sequence不为0.    sleep(2);//缓冲线程创建}if(F_improve_ThrdPoll == 1 ){//检测到备用线程池的创建while(task_wait_queue.length() == 0 && task_doing_queue.length() == 0){//也就是当前任务等待队列和任务执行队列都没有任务时printf("no task!.\n");usleep(500000);wait_time++;//计时等待一段时间if(wait_time == NO_TASK_TIMEOUT){this->mutex->unlock();ReleaseSubThreadPool();//释放备用线程池printf("release!.\n");F_improve_ThrdPoll = 2;wait_time = 0;break;}}wait_time = 0;}if(F_improve_ThrdPoll != 2)this->mutex->unlock();}return API_SUCCESS;}//自动计算是否需要创建新的线程池的简单机制,后续会结合读取当前CPU的使用率进一步优化此机制
API_RETURN_TYPE_T ThreadPool::AutoComputeOptimumThreadNum(int wait_que_num,int &_u4sequence)
{if(wait_que_num >= 4*_num_threads){_u4sequence = _num_threads;}else if(wait_que_num >= 2*_num_threads){_u4sequence = _num_threads/2;}else{_u4sequence = 0;}return API_SUCCESS;
}//释放备用线程池,待优化API_RETURN_TYPE_T ThreadPool::ReleaseSubThreadPool()
{this->mutex->lock();CANCEL_SIGNAL = 1;this->mutex->unlock();task_cond->broadcast();for(int i = 0;i < _num_threads;i++){if(RET_OK != pthread_cancel(_thread_bak[i])){return API_FAIL;}}this->mutex->lock();printf("4444.\n");CANCEL_SIGNAL = 0;this->mutex->unlock();return API_SUCCESS;
}
//摧毁线程池,待优化
API_RETURN_TYPE_T ThreadPool::DestroyThreadPool()
{//first ,destroy manager thread.if(RET_OK != pthread_cancel(taskqueue_thread)){return API_FAIL;}return API_SUCCESS;
}API_RETURN_TYPE_T ThreadPool::ThreadJoin()
{for(int i = 0;i < _num_threads;i++){pthread_join(_thread[i],NULL);}pthread_join(taskqueue_thread,NULL);return API_SUCCESS;}
//用户调用此函数接口唤醒
API_RETURN_TYPE_T ThreadPool::wakeupThread(TaskFuncCallback p_func)
{printf("wakeupThread in .\n");this->mutex->lock();TASK_QUEUE_T task;task.cTaskFunc = p_func;//将函数执行地址赋值到队列元素中task.sTask = TASK_WAIT;if(task_wait_queue.length() < MAX_TASK_NUM ){   this->task_wait_queue.pushBack(task); //push任务到等待任务队列中}else{//线程池数量过多,此机制后续会优化printf("Current Thread Buffer is full!Please wait a moment!\n");this->mutex->unlock();return API_FAIL;}this->mutex->unlock();return API_SUCCESS;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249

下面新加的关于LVQueue的实现:

#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDEDtemplate < typename T >
class LVQueue {friend struct Iterator;struct Item {T value;Item * next;Item * prev;Item(T & v) : value(v), next(NULL), prev(NULL) {}};Item * head;Item * tail;int count;Item * remove(Item * p) {if (!p)return NULL;if (!p->prev)head = p->next;elsep->prev->next = p->next;if (!p->next)tail = p->prev;elsep->next->prev = p->prev;p->next = NULL;p->prev = NULL;count--;if (count == 0) {head = tail = NULL;}return p;}void moveToHead(Item * item) {Item * p = remove(item);if (head) {head->prev = p;p->next = head;head = p;} else {head = tail = p;}count++;}
public:struct Iterator {private:LVQueue * queue;Item * currentItem;public:Iterator(const Iterator & v) {queue = v.queue;currentItem = v.currentItem;}Iterator(LVQueue * _queue) : queue(_queue), currentItem(NULL) {}T get() { return currentItem ? currentItem->value : T(); }void set(T value) { if (currentItem) currentItem->value = value; }bool next() {if (!currentItem) {// first timecurrentItem = queue->head;} else {// continuecurrentItem = currentItem->next;}return currentItem != NULL;}T remove() {if (!currentItem)return T();Item * next = currentItem->next;Item * p = queue->remove(currentItem);currentItem = next;T res = p->value;delete p;return res;}void moveToHead() {if (currentItem)queue->moveToHead(currentItem);}};public:Iterator iterator() { return Iterator(this); }LVQueue() : head(NULL), tail(NULL), count(0) {}~LVQueue() { clear(); }
//    T & operator [] (int index) {
//        Item * p = head;
//        for (int i = 0; i < index; i++) {
//            if (!p)
//                return
//        }
//    }int length() { return count; }void pushBack(T item) {Item * p = new Item(item);if (tail) {tail->next = p;p->prev = tail;tail = p;} else {head = tail = p;}count++;}void pushFront(T item) {Item * p = new Item(item);if (head) {head->prev = p;p->next = head;head = p;} else {head = tail = p;}count++;}T popFront() {if (!head)return T();Item * p = remove(head);T res = p->value;delete p;return res;}T popBack() {if (!tail)return T();Item * p = remove(tail);T res = p->value;delete p;return res;}void clear() {while (head) {Item * p = head;head = p->next;delete p;}head = NULL;tail = NULL;count = 0;}
};#endif // LVQUEUE_H_INCLUDED
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154

以下是简单的test程序:

    ThreadPool *thread3 = ThreadPool::createThreadPool(8);//得到线程池对象printf("task coming.\n");//test threadpoolfor(int i = 0;i < 15;i++){thread3->wakeupThread(thread11_func);//每隔一秒唤醒线程,thread11_func一个函数的地址sleep(1);thread3->wakeupThread(thread3_func);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

下面是test程序运行的结果,线程唤醒无一秒间隔

num = 8.
task coming.
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
wakeupThread in .
thread 0xd528c700 begin
thread 0xd528c700 is waiting
thread 0xd4a8b700 begin
thread 0xd4a8b700 is waiting
thread 0xd428a700 begin
thread 0xd428a700 is waiting
thread 0xd5a8d700 begin
thread 0xd5a8d700 is waiting
thread 0xd628e700 begin
thread 0xd628e700 is waiting
thread 0xd6a8f700 begin
thread 0xd6a8f700 is waiting
thread 0xd7290700 begin
thread 0xd7290700 is waiting
thread 0xd7a91700 begin
thread 0xd7a91700 is waiting
managerThread!.
signal cond.
num = 4.
thread 0xd2286700 begin
thread 0xd2a87700 begin
thread 0xd3288700 begin
thread 0xd3a89700 begin
thread 0xd2286700 is waiting
thread 0xd2a87700 is waiting
thread 0xd3288700 is waiting
thread 0xd3a89700 is waiting
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
signal cond.
managerThread!.
managerThread!.
managerThread!.
managerThread!.
managerThread!.
rate = 4.82897
this is 0 task  thread.
wait len =22.
thread 0xd528c700 done.length() = 7.
thread 0xd528c700 begin
thread 0xd528c700 is waiting
managerThread!.
signal cond.
rate = 4.64646
this is 1 task  thread.
wait len =21.
thread 0xd4a8b700 done.length() = 7.
thread 0xd4a8b700 begin
thread 0xd4a8b700 is waiting
managerThread!.
signal cond.
rate = 4.64646
this is 2 task  thread.
wait len =20.
thread 0xd428a700 done.length() = 7.
thread 0xd428a700 begin
thread 0xd428a700 is waiting
managerThread!.
signal cond.
rate = 4.25101
this is 3 task  thread.
wait len =19.
thread 0xd5a8d700 done.length() = 7.
thread 0xd5a8d700 begin
thread 0xd5a8d700 is waiting
managerThread!.
signal cond.
rate = 4.23387
this is 4 task  thread.
wait len =18.
thread 0xd628e700 done.length() = 7.
thread 0xd628e700 begin
thread 0xd628e700 is waiting
managerThread!.
signal cond.
rate = 4.04858
this is 5 task  thread.
wait len =17.
thread 0xd6a8f700 done.length() = 7.
thread 0xd6a8f700 begin
thread 0xd6a8f700 is waiting
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

可以看到一次性唤醒了30个线程,创建了8个线程的线程池,后来通过优化计算又新增了4个线程到当前线程池中,每唤醒一个线程执行任务,大概是6s的时间,执行完后,又进入等待唤醒信号的状态。管理线程检测到当前所有线程都在执行,便会阻塞当前signal行为,直到有空余线程,马上signal。

这些源代码还有一些数据类型的封装还没公布出来,因为还在优化中,所以准备等到优化完毕,将会把完整的源代码交到GitHub上托管,小弟资历尚浅,如有出错的地方,烦请不吝赐教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385001.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vfprintf()函数

函数声明&#xff1a;int vfprintf(FILE *stream, const char *format, va_list arg) 函数参数&#xff1a; stream—这是指向了FILE对象的指针&#xff0c;该FILE对象标识了流。 format—c语言字符串&#xff0c;包含了要被写入到流stream中的文本。它可以包含嵌入的format标签…

TCP粘包问题分析和解决(全)

TCP通信粘包问题分析和解决&#xff08;全&#xff09;在socket网络程序中&#xff0c;TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程&#xff0c;收发两端&#xff08;客户端和服务器端&#xff09;都要有成对的socket&#xff0c;因此&#xff0c;发送端为了将…

UML类图符号 各种关系说明以及举例

UML中描述对象和类之间相互关系的方式包括&#xff1a;依赖&#xff0c;关联&#xff0c;聚合&#xff0c;组合&#xff0c;泛化&#xff0c;实现等。表示关系的强弱&#xff1a;组合>聚合>关联>依赖 相互间关系 聚合是表明对象之间的整体与部分关系的关联&#xff0c…

ESP传输模式拆解包流程

一、 ESP简介ESP&#xff0c;封装安全载荷协议(Encapsulating SecurityPayloads)&#xff0c;是一种Ipsec协议&#xff0c;用于对IP协议在传输过程中进行数据完整性度量、来源认证、加密以及防回放攻击。可以单独使用&#xff0c;也可以和AH一起使用。在ESP头部之前的IPV4…

linux内核netfilter模块分析之:HOOKs点的注册及调用

1: 为什么要写这个东西?最近在找工作,之前netfilter 这一块的代码也认真地研究过&#xff0c;应该每个人都是这样的你懂 不一定你能很准确的表达出来。 故一定要化些时间把这相关的东西总结一下。 0&#xff1a;相关文档linux 下 nf_conntrack_tuple 跟踪记录 其中可以根据内…

网络抓包工具 wireshark 入门教程

Wireshark&#xff08;前称Ethereal&#xff09;是一个网络数据包分析软件。网络数据包分析软件的功能是截取网络数据包&#xff0c;并尽可能显示出最为详细的网络数据包数据。Wireshark使用WinPCAP作为接口&#xff0c;直接与网卡进行数据报文交换。网络管理员使用Wireshark来…

结构体中指针

结构体中带有指针的情况 #include<stdio.h>struct man {char *name;int age; };int main() {struct man m {"tom",20};printf("name %s, age %d\n",m.name,m.age);return 0; } 运行结果&#xff1a; exbotubuntu:~/wangqinghe/C/20190714$ gcc st…

python使用opencv提取视频中的每一帧、最后一帧,并存储成图片

提取视频每一帧存储图片 最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&…

线程间通信之eventfd

线程间通信之eventfd man手册中的解释&#xff1a; eventfd()创建了一个“eventfd对象”&#xff0c; 通过它能够实现用户态程序间(我觉得这里主要指线程而非进程)的等待/通知机制&#xff0c;以及内核态向用户态通知的机制&#xff08;未考证&#xff09;。 此对象包含了一个…

定时器timerfd

1.为什么要加入此定时器接口 linux2.6.25版本新增了timerfd这个供用户程序使用的定时接口&#xff0c;这个接口基于文件描述符&#xff0c;当超时事件发生时&#xff0c;该文件描述符就变为可读。我首次接触这个新特性是在muduo网络库的定时器里看到的&#xff0c;那么新增一个…

timerfd与epoll

linux timerfd系列函数总结 网上关于timerfd的文章很多&#xff0c;在这儿归纳总结一下方便以后使用&#xff0c;顺便贴出一个timerfd配合epoll使用的简单例子 一、timerfd系列函数 timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符&#xff0c;通过文…

linux僵尸进程产生的原因以及如何避免产生僵尸进程defunct

给进程设置僵尸状态的目的是维护子进程的信息&#xff0c;以便父进程在以后某个时间获取。这些信息包括子进程的进程ID、终止状态以及资源利用信息(CPU时间&#xff0c;内存使用量等等)。如果一个进程终止&#xff0c;而该进程有子进程处于僵尸状态&#xff0c;那么它的所有僵尸…

linux下僵尸进程(Defunct进程)的产生与避免

在测试基于 DirectFBGstreamer 的视频联播系统的一个 Demo 的时候&#xff0c;其中大量使用 system 调用的语句&#xff0c;例如在 menu 代码中的 system("./play") &#xff0c;而且多次执行&#xff0c;这种情况下&#xff0c;在 ps -ef 列表中出现了大量的 defunc…

读过的最好的epoll讲解

首先我们来定义流的概念&#xff0c;一个流可以是文件&#xff0c;socket&#xff0c;pipe等等可以进行I/O操作的内核对象。 不管是文件&#xff0c;还是套接字&#xff0c;还是管道&#xff0c;我们都可以把他们看作流。 之后我们来讨论I/O的操作&#xff0c;通过read&#xf…

C语言指针转换为intptr_t类型

C语言指针转换为intptr_t类型 1、前言 今天在看代码时&#xff0c;发现将之一个指针赋值给一个intptr_t类型的变量。由于之前没有见过intptr_t这样数据类型&#xff0c;凭感觉认为intptr_t是int类型的指针。感觉很奇怪&#xff0c;为何要将一个指针这样做呢&#xff1f;如是果…

北京加密机现场select问题

问题描述 北京项目通过调用我们提供的库libsigxt.a与加密机通信&#xff0c;c/s架构&#xff0c;客户端启用多个线程&#xff0c;每个线程流程有以下三步&#xff0c;连接加密机&#xff0c;签名&#xff0c;关闭链接。在正常运行一段时间后会出现不能连接加密机服务问题。 连…

处理SIGCHLD信号

在上一讲中&#xff0c;我们使用fork函数得到了一个简单的并发服务器。然而&#xff0c;这样的程序有一个问题&#xff0c;就是当子进程终止时&#xff0c;会向父进程发送一个SIGCHLD信号&#xff0c;父进程默认忽略&#xff0c;导致子进程变成一个僵尸进程。僵尸进程一定要处理…

nginx源码阅读(一).综述

前言 nginx作为一款开源的轻量级高性能web服务器,是非常值得立志从事服务端开发方向的人学习的。现今nginx的最新版本是nginx-1.13.6,代码量也日渐庞大,但是由于其核心思想并没改变,为了降低阅读难度,我选择的是nginx-1.0.15版本,并且由于时间和水平有限,重点关注的是nginx的启…

系统级性能分析工具perf的介绍与使用

系统级性能优化通常包括两个阶段&#xff1a;性能剖析&#xff08;performance profiling&#xff09;和代码优化。 性能剖析的目标是寻找性能瓶颈&#xff0c;查找引发性能问题的原因及热点代码。 代码优化的目标是针对具体性能问题而优化代码或编译选项&#xff0c;以改善软…

C/C++内存问题检查利器——Purify

C&#xff0f;C内存问题检查利器——Purify 一、 引言 我们都知道软件的测试&#xff08;在以产品为主的软件公司中叫做QA—Quality Assessment&#xff09;占了整个软件工程的30% -50%&#xff0c;但有这么一种说法&#xff0c;即使是最优秀测试专家设计出来的测试…