随着互联网技术的不断发展,服务端编程技术也得到了很大的发展,成为了传统软件开发的重要领域。在服务端编程技术中,多线程编程技术是一个重要的组成部分。而其中的linux多线程服务端编程技术,更是目前比较流行的一种编程方式。
成都创新互联是专业的巴宜网站建设公司,巴宜接单;提供网站建设、成都网站制作,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行巴宜网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
本篇文章就将结合实际项目开发,探讨Linux多线程服务端编程的一些实践技巧以及注意事项。
一、多线程服务端编程架构
在构建多线程服务端应用时,需要考虑应用的架构。一般而言,多线程服务端应用的架构应该包含以下几个组件:
1. 并发请求接收器:可以使用select、epoll 或者libev等等工具库来实现。其主要作用是接收并行的客户请求。
2. 主逻辑处理器:在收到客户请求后,主逻辑处理器将处理客户请求并且返回响应。这个部分更好采用多线程的方式来实现,以达到更高的并发性能。
3. 数据库连接池:多线程服务端应用需要与数据库或者其他外部资源交互。因此需要使用连接池来管理和优化数据库连接的使用。
4. 缓存机制:缓存是提高应用性能和响应速度的重要手段。多线程服务端应用中,缓存机制可以存储应用中经常使用的数据,避免频繁地使用外部资源。可以采用Memcached、Redis等缓存服务器来实现。
二、多线程服务端编程的实际应用
在实际开发时,应该使用优秀的多线程编程技术来提高服务端应用的并发性。若格外关注这方面的编码细节,能够实现应用很高的响应速度以及更高的并发性能。下面将深入探讨在实际开发背景下如何使用多线程编程技术。
1. 使用线程池
线程池可以有效地提高多线程服务端的性能。由于线程的创建和销毁是相对较慢的操作,因此,可以预先分配一定数量的线程,把它们放在一个队列中。当需要多线程处理请求时,就从队列中获取一个空闲的线程来执行所需要的操作,处理完后再放回队列中。
代码示例:
“`cpp
#include
#include
#include
#include
#define MAX_THREADS 16
#define MAX_QUEUE 65535
typedef struct task_struct {
void *(*func)(void *arg);
void *arg;
} task_t;
struct thread_pool {
pthread_mutex_t lock;
pthread_cond_t notify;
pthread_t *threads;
task_t *queue;
int thread_count;
int task_count;
int head;
int tl;
int shutdown;
int started;
};
typedef struct thread_pool thread_pool_t;
// 初始化线程池
void thread_pool_init(thread_pool_t *pool, int threads_count);
// 关闭线程池
void thread_pool_shutdown(thread_pool_t *pool);
// 向任务队列放一个任务
int thread_pool_push(thread_pool_t *pool, void *(*func)(void *), void *arg);
static void *thread_routine(void *arg);
void thread_pool_init(thread_pool_t *pool, int threads_count) {
pthread_mutex_init(&pool->lock, NULL);
pthread_cond_init(&pool->notify, NULL);
pool->threads = (pthread_t*)malloc(sizeof(pthread_t)*threads_count);
pool->queue = (task_t*)malloc(sizeof(task_t)*MAX_QUEUE);
pool->thread_count = threads_count;
pool->task_count = 0;
pool->shutdown = 0;
pool->started = 0;
pool->head = pool->tl = 0;
for (int i = 0; i
pthread_create(&pool->threads[i], NULL, thread_routine, (void*)pool);
}
}
// 添加一个任务到线程池
int thread_pool_push(thread_pool_t *pool, void *(*func)(void *), void *arg) {
pthread_mutex_lock(&pool->lock);
if (pool->task_count == MAX_QUEUE) {
pthread_mutex_unlock(&pool->lock);
return -1;
}
pool->queue[pool->tl].func = func;
pool->queue[pool->tl].arg = arg;
pool->tl = (pool->tl + 1) % MAX_QUEUE;
pool->task_count++;
pthread_cond_signal(&pool->notify);
pthread_mutex_unlock(&pool->lock);
return 0;
}
void thread_pool_shutdown(thread_pool_t *pool) {
pthread_mutex_lock(&pool->lock);
pool->shutdown = 1;
pthread_mutex_unlock(&pool->lock);
pthread_cond_broadcast(&pool->notify);
for (int i = 0; i thread_count; ++i) {
pthread_join(pool->threads[i], NULL);
}
free(pool->threads);
for (int i = 0; i task_count; ++i) {
free(pool->queue[i].arg);
}
free(pool->queue);
pthread_mutex_destroy(&pool->lock);
pthread_cond_destroy(&pool->notify);
}
static void *thread_routine(void *arg) {
thread_pool_t *pool = (thread_pool_t*) arg;
while (1) {
pthread_mutex_lock(&pool->lock);
while (pool->task_count == 0 && !pool->shutdown) {
pthread_cond_wt(&pool->notify, &pool->lock);
}
if (pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
task_t task;
task.func = pool->queue[pool->head].func;
task.arg = pool->queue[pool->head].arg;
pool->head = (pool->head + 1) % MAX_QUEUE;
pool->task_count–;
pthread_mutex_unlock(&pool->lock);
(*(task.func))(task.arg);
}
pthread_exit(NULL);
}
“`
2. 使用互斥锁(mutex)和条件变量(condition variable)
在多线程编程中,互斥锁和条件变量是实现线程同步的常见方式。当不同的线程需要访问同一个共享资源时,为了避免出现不一致的情况,必须进行同步。而互斥锁和条件变量则是用来协调线程间的同步和互斥访问。
代码示例:
“`cpp
#include
#include
#include
#include
pthread_mutex_t mutex;
int num;
int count;
void * thread_work(void *arg)
{
int tid = *(int*)arg;
for (int i = 0; i
pthread_mutex_lock(&mutex);
num = tid;
count++;
printf(“thread #%d, num=%d, count=%d\n”, tid, num, count);
pthread_mutex_unlock(&mutex);
}
pthread_exit(NULL);
}
int mn()
{
pthread_t threads[16];
int ids[16];
pthread_mutex_init(&mutex, NULL);
for (int i = 0; i
ids[i] = i;
pthread_create(&threads[i], NULL, thread_work, (void*)&ids[i]);
}
for (int i = 0; i
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&mutex);
return 0;
}
“`
3. 多线程编译优化
在多线程编程中,编译器对代码进行的优化只是单线程编程中的一部分。但是编译优化涉及到包括多线程编程在内的各种应用,它们可以使用多种工具来帮助提高并发性能。
一些常见的编译优化选项包括:
– -O3:这个选项会开启所有可能的优化选项,包括常量传递、内联函数、死代码削减、函数实参优化等等。
– -march=native:这个选项告诉编译器根据系统内存和CPU架构,使用它认为更优的指令集。
– -pthread:使用这个选项将会使编译器在链接时启用 pthread 库。
三、
相关问题拓展阅读:
主要是两个问题,
任务调度
和oversubscription。
openmp默认使用的schedule是取决于
编译器
实现的。gcc默认使用schedule(dynamic,1),也就是动态调度并且块大小是1。在你的程序里面,这种调度是及其低效的,看代码都能预期到,不太可能比
单线程
快。
动态调度的一种简单理解方式是,计算任务存在一个任务队列里面,你的
for循环
每一个i值对应一个计算任务。每个线程每次提取一批任务,然后计算。“一批”是多少呢?就是前面说的块大小,在你的程序里面是1。提取任务需要什么操作呢?因为这个任务队列是多线程共享的,提取任务前必须加锁,读取一批,从队档梁握列中移除,然后解锁。说到这里,你应该已经知道原因了。
你的线程一次只提取一次计算任务,这个任务还完成得很快。然后所有的16个线程排着队,逐个去加锁,抢任务渣局,然后解锁让其它线程继续抢。然后马上发现这个任务很快,又要重新去排队等任务,始终处于饥饿状态。注意排队的时候可能也是要占cpu的,因为使用了busy
wait,所以可能你看来十六核满负荷,但是其实啥也没干。
我的建议就是,行庆使用static
schedule,或者增加dynamic
schedule的块大小,比如1024,取决于你循环多少次。一般
如果你知道
每次循环的执行时间基本都是一样,并且是专用服务器设置好affinity,无其它负荷无oversubscription无numa问题的话,static
schedule会是个比较好的选择。这样每个线程做哪些任务只需要进行一次分配,最小化了openmp本身的消耗。
还有一个非常重要的问题!
数值计算
不要使用
cpu超线程
!cpu的超线程对于数值计算基本是有害无益的,线程数不要大于实际核数,否则就是oversubscription。你这已经是非常严重的oversubscription了。数值计算专用的话,建议直接关闭服务器bios里面的超线程选项。
关于linux多线程服务端…的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
成都网站营销推广找创新互联,全国分站站群网站搭建更好做SEO营销。
创新互联(www.cdcxhl.com)四川成都IDC基础服务商,价格厚道。提供成都服务器托管租用、绵阳服务器租用托管、重庆服务器托管租用、贵阳服务器机房服务器托管租用。
分享文章:Linux多线程服务端编程实践 (linux多线程服务端…)
文章URL:http://www.mswzjz.cn/qtweb/news2/279602.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能