排队模型回顾
问题分析
如何模拟工作窗口 (即:如何模拟工作人员) ?
如何模拟等待中的顾客 (即:如何模拟待执行的任务)?
如何维护等待队列?(队列数据结构?)
工作时间如何模拟?新顾客如何模拟?
新概念:线程池
背景
- 线程的 创建/销毁 会消耗额外的资源 (时间 & 空间)
- 进程中不能无限制的创建新的线程 (太多线程反而降低执行效率)
方案:将多个线程预先存储在一个 "池子" 内,当需要线程时直接从 "池子" 取出
优势:降低 创建/消耗 线程所带来的时间开销及系统资源开销
解决方案设计
利用线程池模拟工作窗口,每个线程表示一个工作人员
定义任务结构体 (CurTask) 模拟顾客 (CurTask 变量表示具体客户)
使用队列数据结构表示等待队列,队列中存储 CurTask 变量 (顾客排队模拟)
线程从队列中取出任务执行 (服务客户模拟)
线程安全性数据结构
数据结构具有原子性!即:数据结构的操作具有原子性!
实现细节
各个线程从队列中取任务时是互斥操作 (每次只能一个线程操作队列)
如果队列为空,那么线程处于等待状态
主线程可以动态产生任务,并将任务放入等待队列
全局统计变量,用于统计工作量
问题升华:生产消费者问题
排队模型应用实验
list.h
#ifndef LIST_H
#define LIST_H
#define OffsetOf(type, member) ((unsigned)&(((type*)0)->member))
#define ContainerOf(ptr, type, member) \
({ \
const typeof(((type*)0)->member)* __mptr = (ptr); \
(type*)((char*)__mptr - OffsetOf(type, member)); \
})
#define IsEqual(a, b) (a == b)
typedef struct _ListNode {
struct _ListNode* next;
struct _ListNode* prev;
} ListNode;
typedef ListNode List;
#define List_ForEach(list, pos) for(pos=(list)->next; !IsEqual(list, pos); pos=pos->next)
#define List_Node(ptr, type, member) ContainerOf(ptr, type, member)
void List_Init(List* list);
void List_Add(List* list, ListNode* node);
void List_AddTail(List* list, ListNode* node);
void List_AddBefore(ListNode* before, ListNode* node);
void List_AddAfter(ListNode* after, ListNode* node);
void List_DelNode(ListNode* node);
void List_Replace(ListNode* old, ListNode* node);
int List_IsLast(List* list, ListNode* node);
int List_IsEmpty(List* list);
#endif
list.c
#include "list.h"
void List_Init(List* list)
{
list->next = list;
list->prev = list;
}
static void _List_Add(ListNode* node, ListNode* prev, ListNode* next)
{
next->prev = node;
node->next = next;
prev->next = node;
node->prev = prev;
}
void List_Add(List* list, ListNode* node)
{
_List_Add(node, list, list->next);
}
void List_AddTail(List* list, ListNode* node)
{
_List_Add(node, list->prev, list);
}
void List_AddBefore(ListNode* before, ListNode* node)
{
_List_Add(node, before->prev, before);
}
void List_AddAfter(ListNode* after, ListNode* node)
{
_List_Add(node, after, after->next);
}
static void _List_Del(ListNode* prev, ListNode* next)
{
prev->next = next;
next->prev = prev;
}
void List_DelNode(ListNode* node)
{
_List_Del(node->prev, node->next);
node->prev = 0;
node->next = 0;
}
void List_Replace(ListNode* old, ListNode* node)
{
node->next = old->next;
node->next->prev = node;
node->prev = old->prev;
node->prev->next = node;
old->prev = 0;
old->next = 0;
}
int List_IsLast(List* list, ListNode* node)
{
return IsEqual(list, node->next);
}
int List_IsEmpty(List* list)
{
ListNode* next = list->next;
return IsEqual(next, list) && IsEqual(next, list->prev);
}
queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include "list.h"
typedef ListNode QueueNode;
typedef void* Queue;
Queue Queue_Create();
void Queue_Destroy(Queue queue);
int Queue_IsEmpty(Queue queue);
int Queue_IsContained(Queue queue, QueueNode* node);
void Queue_Add(Queue queue, QueueNode* node);
QueueNode* Queue_Front(Queue queue);
QueueNode* Queue_Remove(Queue queue);
int Queue_Length(Queue queue);
void Queue_Rotate(Queue queue);
#endif
queue.c
#include <pthread.h>
#include <malloc.h>
#include "queue.h"
typedef struct {
ListNode head;
pthread_mutex_t mutex;
int length;
} TQueue;
Queue Queue_Create()
{
TQueue* ret = malloc(sizeof(*ret));
if( ret )
{
pthread_mutexattr_t attr = {0};
pthread_mutexattr_init(&attr);
pthread_mutex_init(&ret->mutex, &attr);
List_Init((List*)ret);
ret->length = 0;
}
return ret;
}
void Queue_Destroy(Queue queue)
{
TQueue* q = queue;
pthread_mutex_destroy(&q->mutex);
free(q);
}
int Queue_IsEmpty(Queue queue)
{
TQueue* q = queue;
int ret = 0;
pthread_mutex_lock(&q->mutex);
ret = List_IsEmpty((List*)q);
pthread_mutex_unlock(&q->mutex);
return ret;
}
int Queue_IsContained(Queue queue, QueueNode* node)
{
TQueue* q = queue;
ListNode* pos = NULL;
int ret = 0;
pthread_mutex_lock(&q->mutex);
List_ForEach((List*)q, pos)
{
if( ret = IsEqual(pos, node) )
{
break;
}
}
pthread_mutex_unlock(&q->mutex);
return ret;
}
void Queue_Add(Queue queue, QueueNode* node)
{
TQueue* q = queue;
pthread_mutex_lock(&q->mutex);
List_AddTail((List*)q, node);
q->length++;
pthread_mutex_unlock(&q->mutex);
}
QueueNode* Queue_Front(Queue queue)
{
TQueue* q = queue;
QueueNode* ret = NULL;
pthread_mutex_lock(&q->mutex);
ret = q->head.next;
pthread_mutex_unlock(&q->mutex);
return ret;
}
QueueNode* Queue_Remove(Queue queue)
{
TQueue* q = queue;
QueueNode* ret = NULL;
pthread_mutex_lock(&q->mutex);
if( q->length > 0 )
{
ret = q->head.next;
List_DelNode(ret);
q->length--;
}
pthread_mutex_unlock(&q->mutex);
return ret;
}
int Queue_Length(Queue queue)
{
TQueue* q = queue;
int ret = 0;
pthread_mutex_lock(&q->mutex);
ret = q->length;
pthread_mutex_unlock(&q->mutex);
return ret;
}
void Queue_Rotate(Queue queue)
{
TQueue* q = queue;
pthread_mutex_lock(&q->mutex);
if( q->length > 0 )
{
QueueNode* node = Queue_Remove(q);
Queue_Add(q, node);
}
pthread_mutex_unlock(&q->mutex);
}
conint.h
#ifndef CONINT_H
#define CONINT_H
typedef void* ConInt;
ConInt ConInt_Create(int v);
void ConInt_Destroy(ConInt ci);
void ConInt_Add(ConInt ci, int v);
void ConInt_Sub(ConInt ci, int v);
int ConInt_Value(ConInt ci);
#endif
conint.c
#include <pthread.h>
#include <stdlib.h>
#include "conint.h"
typedef struct
{
pthread_mutex_t mutex;
int v;
} TConInt;
ConInt ConInt_Create(int v)
{
TConInt* tci = (TConInt*)malloc(sizeof(TConInt));
if(tci)
{
pthread_mutex_init(&tci->mutex, NULL);
tci->v = v;
}
return tci;
}
void ConInt_Destroy(ConInt ci)
{
TConInt* tci = ci;
pthread_mutex_destroy(&tci->mutex);
free(ci);
}
void ConInt_Add(ConInt ci, int v)
{
TConInt* tci = ci;
pthread_mutex_lock(&tci->mutex);
tci->v += v;
pthread_mutex_unlock(&tci->mutex);
}
void ConInt_Sub(ConInt ci, int v)
{
ConInt_Add(ci, v);
}
int ConInt_Value(ConInt ci)
{
TConInt* tci = ci;
int ret = 0;
pthread_mutex_lock(&tci->mutex);
ret = tci->v;
pthread_mutex_unlock(&tci->mutex);
return ret;
}
main.c
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include "conint.h"
#include "queue.h"
#define T 5
#define N 10
typedef struct
{
QueueNode node;
void (*task)(void*);
void* arg;
} CusTask;
static Queue g_queue;
static ConInt g_count;
static void customer_task(void* arg)
{
int i = 0;
int v = (long)arg;
printf("begin task: %d\n", v);
for(i = 0; i < v; i++)
{
usleep(rand() % N * 50000);
}
printf("end task: %d\n", v);
}
static void* service_thread(void* arg)
{
printf("begin service: %ld\n", (long)arg);
while( ConInt_Value(g_count) < N )
{
if( Queue_Length(g_queue) )
{
CusTask* t = (CusTask*)Queue_Remove(g_queue);
printf("thread %ld ===> task %ld\n", (long)arg, (long)t->arg);
t->task(t->arg);
free(t);
ConInt_Add(g_count, 1);
}
else
{
usleep(50 * 1000);
}
}
printf("end service: %ld\n", (long)arg);
return NULL;
}
int main(int argc, char* argv[])
{
pthread_t tid[T] = {0};
long i = 0;
g_queue = Queue_Create();
g_count = ConInt_Create(0);
srand(time(NULL));
if(g_queue && g_count)
{
for(i = 0; i < T; i++)
{
pthread_create(&tid[i], NULL, service_thread, (void*)i);
}
for(i = 0; i < N; i++)
{
CusTask* t = (CusTask*)malloc(sizeof(CusTask));
if(t)
{
t->task = customer_task;
t->arg = (void*)i;
Queue_Add(g_queue, (QueueNode*)t);
}
sleep(1);
}
for(i = 0; i < T; i++)
{
pthread_join(tid[i], NULL);
}
ConInt_Destroy(g_count);
Queue_Destroy(g_queue);
}
return 0;
}
ConInt 是我们实现为线程安全的整形,Queue 是我们实现为线程安全的队列,访问共享变量的时候都加了互斥锁保护,这样我们就可以在多线程下安全的使用这两个数据结构了
我们使用线程池的思想,这里是使用数组的方式,预先创建了 5 个线程,这样能避免线程后续多次创建和销毁带来的开销,然后在等待队列中取任务,取到任务则去执行
在主线程中我们塞入了 10 个任务,然后等待 5 个线程运行结束
程序执行结果如下图所示:
可以看出 10 个任务都执行完成了