Linux多线程服务端编程实践 (linux多线程服务端…)

随着互联网技术的不断发展,服务端编程技术也得到了很大的发展,成为了传统软件开发的重要领域。在服务端编程技术中,多线程编程技术是一个重要的组成部分。而其中的Linux多线程服务端编程技术,更是目前比较流行的一种编程方式。

本篇文章就将结合实际项目开发,探讨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 库。

三、

相关问题拓展阅读:

linux多线程为什么单线程执行

主要是两个问题,

任务调度

和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多线程服务端…的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Linux多线程服务端编程实践 (linux多线程服务端…)