linux网络编程学习笔记之五 -----并发机制与线程�
进程线程分配方式
简述下常见的进程和线程分配方式:(好吧,我仅仅是举几个样例作为笔记。。。并发的水太深了,不敢妄谈。。。)
1、进程线程预分配
简言之,当I/O开销大于计算开销且并发量较大时,为了节省每次都要创建和销毁进程和线程的开销。能够在请求到达前预先进行分配。
2、进程线程延迟分配
预分配节省了处理时的负担,但操作系统管理这些进程线程也会带来一定的开销。由此,有个折中的方法是,当某个处理须要花费较长时间的时候,我们创建一个并发的进程或线程来处理该请求。实现也非常easy,在主线程中定时,定时到期,开新分支。
3、前面两者结合
还是举个样例,比方能够:在启动时不进行预分配,某个处理太长,则创建从进程,任务结束后不退出。
多进程与多线程比較
能够參考这篇论文:Linux下多进程和多线程性能分析 和这篇Blog:多进程or多线程 总结起来,在任务运行效率上,在任务量较大(文中单次5k以上),多进程的效率高点,反之,多线程站优势,但总体上出入不大。而在创建和销毁的效率上,线程的优势明显,约为5~6倍。然后在server上,并发量不大(小于几千),预先创建线程也没太大优势,由于动态管理线程的开销亦不可忽略。
线程池
比較全面和概要的介绍能够參看:线程池的介绍及简单实现
基本思路是,预先创建一定数量的线程,让它们处于堵塞状态,占用非常小的内存空间。当任务到来时,选择一个空暇的线程运行,完毕后线程不退出,继续堵塞。池子的创建销毁和管理有一个线程单独完毕。
进一步地,动态地对线程的数量进行管理,负载较大时,添加�线程数量。负载小时,降低之,设法让一段时间不活跃的线程退出,比方让线程在等待下一个请求前先启动一个定时器,若请求到达前定时器到期,则线程退出。
对于处理时间短,处理数目巨大的情况,线程池有天然优势。尤其是对性能要求高的应用或者突发性大规模请求,比方电商秒杀神马的。
线程池的实现能够參考libthreadpool,一个开源的库,sourceforge上能找到源代码
我用简单的模型实现了一个,功能上基本满足,主从线程的流程例如以下图所看到的,唤醒空暇线程的时候不加以区分,即steven说的惊群,这会一定程度上的损失性能。与之相应的是有主线程採取一定的方式对空暇线程的唤醒进行调度以均衡负载和工作量。以下的代码是我的1.0版(改得太乱了,看官们勿怪),更进一步的功能,如动态地改变池子的尺寸,兴许继续完好
pthread_pool.h
#ifndef _PTHREAD_POOL_H_
#define _PTHREAD_POOL_H_ #include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h> #define MAX_PTHREAD_NUM 100 typedef struct task_node{
void * (*func)(void * p);
void * arg;
struct task_node* next;
}task_node; typedef struct p_pool{
pthread_cond_t cond;
pthread_mutex_t mutex;
task_node *head, *tail;
pthread_t *p_tid; //mark , unsigned long
int max_num;
int current_num;
int working_num;
int if_destory;
int decrease;
}p_pool; p_pool * pool = NULL; void pool_init(int pthread_num);
void *pthread_process(void *arg);
int add_task(void * (*func)(void *p), void *arg);
void pool_destory(); #endif
pthread_pool.c
/*
* a simple thread pool
* Mon Jun 9 21:44:36 CST 2014
* by Simon Xia
*/
#include"pthread_pool.h" /* each worker thread's thread function to handle the task */
void *pthread_process(void *arg)
{
task_node *tmp = NULL; // printf("Now in the %lu thread\n", pthread_self());
while (1) {
pthread_mutex_lock(&pool->mutex);
// printf("%lu thread get lock\n", pthread_self()); while (!pool->head && !pool->if_destory/* && !pool->decrease*/) { //While ! 用是否有任务来控制
// printf("%lu thread will wait\n", pthread_self());
pthread_cond_wait(&pool->cond, &pool->mutex);
} // printf("%lu thread: signal is coming\n", pthread_self());
if (pool->if_destory /*|| pool->decrease*/)
break; tmp = pool->head; pool->head = pool->head->next;
// pool->working_num++; pthread_mutex_unlock(&pool->mutex);
// printf("%lu thread pick task from queue\n", pthread_self());
(tmp->func)(tmp->arg);
// printf("%lu thread finish task\n", pthread_self());
free(tmp);
tmp = NULL; //mark /*
pthread_mutex_lock(&pool->mutex);
pool->working_num--;
pthread_mutex_unlock(&pool->mutex);
*/ }
pthread_mutex_unlock(&pool->mutex);//先解锁!!
printf("%lu thread will exit\n", pthread_self());
pthread_exit(NULL);
} /* main thread function to manage the thread pool */
/*
void *pthread_main(void *arg)
{
printf("This is main thread\n");
int i;
while (1)
{
usleep(50000);
pthread_mutex_lock(&pool->mutex);
if (pool->if_destory)
break;
if (pool->working_num == pool->current_num) {
for (i = pool->current_num; i < 2 * pool->current_num; i++)
pthread_create(&pool->p_tid[i], NULL, pthread_process, NULL);
pool->current_num *= 2;
printf("The number of thread has been enlarged to %d\n", pool->current_num);
}
else if (pool->working_num <= pool->current_num / 4){
pool->decrease = 1;
pthread_mutex_unlock(&pool->mutex);
for (i = 0; i < pool->current_num / 2; i++)
pthread_cond_signal(&pool->cond);
pool->current_num /= 2;
pool->decrease = 0;
printf("The number of thread has been decrease to %d\n", pool->current_num);
}
pthread_mutex_unlock(&pool->mutex);
}
pthread_exit(NULL);
}
*/ /* Initialize the thread pool
* Input: number of worker thread
*/
void pool_init(int pthread_num)
{
int i = 0; pool = (p_pool*)malloc(sizeof(p_pool));
pthread_mutex_init(&pool->mutex, NULL);
pthread_cond_init(&pool->cond, NULL);
pool->head = pool->tail = NULL;
pool->max_num = MAX_PTHREAD_NUM;
pool->current_num = pthread_num;
pool->working_num = 0;
pool->if_destory = 0;
pool->decrease = 0;
pool->p_tid = (pthread_t*)malloc(pthread_num * sizeof(pthread_t)); // pthread_create(&pool->p_tid[i], NULL, pthread_main, NULL); for (i = 0; i < pthread_num; i++)
pthread_create(&pool->p_tid[i], NULL, pthread_process, NULL); } /* add task into task queue */
int add_task(void * (*func)(void *p), void *arg)
{
task_node *tmp = (task_node*)malloc(sizeof(task_node));
tmp->func = *func; //Mark
tmp->arg = arg;
tmp->next = NULL; pthread_mutex_lock(&pool->mutex);
if (pool->head) {
pool->tail = pool->tail->next = tmp;
}
else {
pool->tail = pool->head = tmp;
} pthread_mutex_unlock(&pool->mutex);
//不加不行?
//printf("Add task %d success!\n",*(int*)tmp->arg);
//sleep(1);
pthread_cond_signal(&pool->cond); tmp = NULL; //can't free
return 0;
} /* destory the pool after all work */
void pool_destory()
{
int i; // pthread_mutex_lock(&pool->mutex);
pool->if_destory = 1;
// pthread_mutex_unlock(&pool->mutex); pthread_cond_broadcast(&pool->cond); for (i = 0; i < pool->current_num; i++)
{
if (!pthread_join(pool->p_tid[i], NULL))
printf("Success to collect thread %lu\n", pool->p_tid[i]);
else
printf("Fail to collect thread %lu\n", pool->p_tid[i]);
} free(pool->p_tid);
free(pool->head);
free(pool->tail); pthread_cond_destroy(&pool->cond);
pthread_mutex_destroy(&pool->mutex); free(pool);
pool = NULL;
}
基于这个线程池的服务端程序:
#include"simon_socket.h" #define SERV_PORT 12345
#define THREAD_CNT 10 extern void pool_init(int );
extern int add_task(void* (*) (void*), void*);
extern void pool_destory(); typedef struct client_info{
int fd;
struct sockaddr_in addr;
struct client_info *next;
}client_info; void *process(void *arg)
{
process_client(((client_info*)arg)->fd, &((client_info*)arg)->addr);
return NULL;
} void sig_int(int signo)
{
pool_destory();
exit(0);
} int main()
{
int sockfd, acfd;
size_t sin_len;
struct sockaddr_in client_addr;
client_info *info_tmp, *info_head = NULL, *info_tail = NULL; signal(SIGINT, sig_int); sin_len = sizeof(struct sockaddr);
sockfd = init_tcp_psock(SERV_PORT);
pool_init(THREAD_CNT); for ( ; ; )
{
if ((acfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_len)) == -1)
{
perror("Accept request failed: ");
return 1;
}
else
printf("Get a connection from %s:%d !\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); info_tmp = (client_info *)malloc(sizeof(client_info));
memset(info_tmp, 0, sizeof(client_info));
info_tmp->fd = acfd;
info_tmp->addr = client_addr;
info_tmp->next = NULL; if (info_head) {
info_tail = info_tail -> next = info_tmp;
}
else{
info_head = info_tail = info_tmp;
}
add_task(process, (void*)info_tmp);
} for (info_tmp = info_head; info_tmp; free(info_tmp))
info_head = info_head -> next; return 0;
}
吐槽下多线程的调试。。。gdb调试多线程有点蛋疼,单步时非常easypthread_cond_wait把pthread_cond_signal的信号错过,出现各种错误,比方Cannot find bounds of current function等,建议大家还是多做输出,或者用日志的方式调。
set scheduler-locking off|on|step 这个命令还是非常实用的,由于用step或者continue命令调试当前被调试线程的时候,其它线程也是同一时候运行的。
详细地:off 不锁定不论什么线程,也就是全部线程都运行,这是默认值。 on 仅仅有当前被调试程序会运行。 step 在单步的时候,除了next过一个函数的情况以外,仅仅有当前线程会运行。
反正我最后是老老实实输出。。各位路过大牛有什么好方法,求指点 ~~
linux网络编程学习笔记之五 -----并发机制与线程�的更多相关文章
- linux网络编程学习笔记之三 -----多进程并发服务端
首先是fork()函数.移步APUE 8.3. 比較清晰的解释能够參考http://blog.csdn.net/lingdxuyan/article/details/4993883和http://w ...
- linux网络编程学习笔记之四 -----多-threaded服务器
对于使用过程中并发.通过实现更轻量级线程. 每个线程都是一个独立的逻辑流. 主题是CPU在执行调度的最小独立单位,这个过程是资源分配单元.当然,这是在微内核操作系统说.总之,这是唯一的一个操作系统内核 ...
- 网络编程学习笔记-TCP拥塞控制机制
为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制.最初由V. Jacobson在1988年的论文中提出的TCP的拥塞控制由“慢启动(Slow start)”和“拥塞避免(Congestion ...
- linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)
errno 在unix系统中对大部分系统调用非正常返回时,通常返回值为-1.并设置全局变量errno(errno.h),如socket(), bind(), accept(), listen(). e ...
- java网络编程学习笔记(四):线程池的实现
package QQ; import java.util.LinkedList; /** * Created by hu on 2015/11/9. */ public class ThreadPoo ...
- 转 网络编程学习笔记一:Socket编程
题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...
- 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理
在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...
- Linux Shell编程学习笔记——目录(附笔记资源下载)
LinuxShell编程学习笔记目录附笔记资源下载 目录(?)[-] 写在前面 第一部分 Shell基础编程 第二部分 Linux Shell高级编程技巧 资源下载 写在前面 最近花了些时间学习She ...
- Linux网络编程学习计划
由于网络编程是很重要的一块,自己这一块也比较欠缺,只知道一些皮毛,从今天开始系统学习<Linux网络编程>一书,全书分为十四个章节: 第一章 概论 P1-16 第二章 UNIX ...
随机推荐
- Sublime Text 皮肤插件安装
安装皮肤, 举例sodahttps://github.com/buymeasoda/soda-themectrl+shift+p => Package Control: Install Pack ...
- SQL中distinct的用法和left join查询的含义
SQL中distinct的用法 1.作用于单列 2.作用于多列 3.COUNT统计 4.distinct必须放在开头 5.其他 在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出 ...
- js 的其它运算符和优先级
三元运算符: 语法为 exp1? exp2:exp3 判断 exp1是true 和 false 如果true,则返回exp2 ,如果false ,则返回exp3 <script> if ...
- oracle常见为题汇总,以及一个简单数据连接操作工厂
本人软件环境:win8.1 64位操作系统,vs2013,安装好了与oracle数据库对应的客户端 连接oracle数据库.以及操作数据库 1.使用IIS建立网站,浏览网页时候,提示“ ...
- WPF感悟(1)
原文地址:http://liutiemeng.blog.51cto.com/120361/91632 1.UI层与逻辑层要尽可能地剥离(解耦). 2.Routed Event和Command比Even ...
- BZOJ 3107 二进制a+b
Description 输入三个整数\(a, b, c\),把它们写成无前导\(0\)的二进制整数.比如\(a=7, b=6, c=9\),写成二进制为\(a=111, b=110, c=1001\) ...
- [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】
题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...
- Highways
poj1751:http://poj.org/problem?id=1751 题意:给你n个城市,每个城市的坐标给你,然后为了是每个城市都连通,需要在已经建了一些街道额基础上,再次建一些街道使其连通, ...
- Struts2的声明式异常处理
在struts2应用程序中你还在使用try catch语句来捕获异常么?如果是这样的,那你OUT啦!struts2支持声明式异常处理,可以再Action中直接抛出异常而交给struts2来 处理,当然 ...
- 【HDOJ】3088 WORM
状态压缩+BFS. /* 3088 */ #include <iostream> #include <cstdio> #include <cstring> #inc ...