掌握Linux多线程编程技能,提高程序效率

作者:沈阳淘贝游戏开发公司 阅读:56 次 发布时间:2023-07-11 00:56:55

摘要:作为一名软件开发者,我们一直在寻找提高程序效率的方法。在这个需要大量数据处理、高效运算的时代,多线程编程技术成为了越来越重要的一部分。Linux系统作为开放源代码的操作系统,具有优秀的多线程编程特性,能够帮助我们更轻松地优化程序性能。本文将围绕着“”这一主题,详细介绍Linux多线程编程的知识点...

作为一名软件开发者,我们一直在寻找提高程序效率的方法。在这个需要大量数据处理、高效运算的时代,多线程编程技术成为了越来越重要的一部分。Linux系统作为开放源代码的操作系统,具有优秀的多线程编程特性,能够帮助我们更轻松地优化程序性能。本文将围绕着“”这一主题,详细介绍Linux多线程编程的知识点和常见应用场景,并且提供一些实用技巧,希望能够帮助大家更好的学习和运用Linux多线程编程技术。

掌握Linux多线程编程技能,提高程序效率

一、多线程编程的基础知识

1. 线程和进程的区别

在进行Linux多线程编程之前,我们需要先了解线程和进程的区别。在操作系统中,进程是资源分配的最小单位,而线程是程序执行的最小单位。一个进程可以包含多个线程,在同一个进程中的多个线程可以共享同一块内存空间,通信很容易。因为线程比进程轻量级,创建和销毁的开销非常小,所以在实现并行计算和任务处理时十分简便。

2. 创建线程的方式

在Linux下,创建线程通常采用pthread库。pthread 库是 Linux 上实现线程的一个标准库。常用的创建线程的函数有两个:pthread_create() 和 pthread_join() 函数。pthread_create() 函数用来创建线程,而 pthread_join() 函数则用来等待线程退出并释放资源。下面是一个最基本的线程创建例子:

```

#include

#include

#include

void *thread_func(void *arg)

{

printf("Hello, world!\n");

pthread_exit(NULL);

}

int main(int argc, char *argv[])

{

pthread_t tid;

if (pthread_create(&tid, NULL, thread_func, NULL) != 0)

{

printf("Failed to create thread\n");

return 1;

}

if (pthread_join(tid, NULL) != 0)

{

printf("Failed to join thread\n");

return 1;

}

return 0;

}

```

这个例子中调用了pthread_create() 函数来创建一个线程,传递参数:线程ID,线程属性,线程函数及参数。最后使用pthread_join() 函数等待该线程结束。

3. 线程同步和互斥

在线程编程中,线程同步和互斥也是比较常见的问题。线程同步的含义是确保两个或多个线程在同一时间共享资源。互斥是线程之间互相排斥,当一个线程使用一个共享资源时,其他线程必须等待,直到该线程完成对资源的使用。下面是一个互斥锁的例子:

```

#include

#include

#include

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static int g_count = 0;

void *thread_func(void *arg)

{

int i;

int id = *(int *)arg;

for (i = 0; i < 100; ++i)

{

pthread_mutex_lock(&mutex);

++g_count;

printf("Thread #%d: g_count = %d\n", id, g_count);

pthread_mutex_unlock(&mutex);

}

pthread_exit(NULL);

}

int main(int argc, char *argv[])

{

pthread_t tids[2];

int ids[2] = {1, 2};

int i;

for (i = 0; i < 2; ++i)

{

if (pthread_create(&tids[i], NULL, thread_func, &ids[i]) != 0)

{

printf("Failed to create thread #%d\n", i+1);

return 1;

}

}

for (i = 0; i < 2; ++i)

{

if (pthread_join(tids[i], NULL) != 0)

{

printf("Failed to join thread #%d\n", i+1);

return 1;

}

}

return 0;

}

```

这个例子中使用了一个静态的互斥锁mutex,使用pthread_mutex_lock()函数加锁,pthread_mutex_unlock() 函数解锁。当一个线程进入printf 代码段时,使用pthread_mutex_lock() 函数将互斥锁锁住,保证另一个线程无法进入,从而保证每个线程更改g_count时,都是在一个线程执行完了之后才执行下一个线程。使用互斥锁可以保证多个线程之间的共享变量不会发生冲突,保证程序的安全性。

4. 线程池

线程池是一种这样的技术,即在程序启动时创建一些线程,然后将一些工作分配给它们,无需每次新建线程。使用线程池技术可以提高程序效率,减少系统开销。有两种类型的线程池:固定线程池和动态线程池。固定线程池创建一定数量的线程,并且在线程池中分配固定个数的任务队列。在任务队列满时新的任务会阻塞等待。而动态线程池则根据任务量的大小自动调节线程数量。下面是一个线程池的例子:

```

#include

#include

#include

typedef struct {

int busy; // 是否正在工作

pthread_t thread; // 工作的线程ID

int sockfd; // 传递给线程的socket

} thread_worker;

static const int kMaxWorkerNum = 8;

static thread_worker *g_thread_workers = NULL;

static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t g_cond = PTHREAD_COND_INITIALIZER;

static int g_timeout = 30; // 空闲线程超时时间

void *thread_func(void *arg)

{

int sockfd;

pthread_t tid = pthread_self();

int i;

while (1)

{

pthread_mutex_lock(&g_mutex);

for (i = 0; i < kMaxWorkerNum; ++i)

if (g_thread_workers[i].thread == tid)

break;

if (i < kMaxWorkerNum)

{

if (g_thread_workers[i].busy == 0) // 线程空闲

{

if (pthread_cond_wait(&g_cond, &g_mutex) != 0) // 等待唤醒

{

perror("Failed to wait on condition");

}

continue;

}

sockfd = g_thread_workers[i].sockfd;

g_thread_workers[i].busy = 0;

pthread_mutex_unlock(&g_mutex);

// 处理任务

// ...

close(sockfd);

}

else

{

pthread_mutex_unlock(&g_mutex);

}

}

return NULL;

}

int add_worker(int sockfd)

{

int i;

for (i = 0; i < kMaxWorkerNum; ++i)

{

if (g_thread_workers[i].busy == 0)

{

g_thread_workers[i].busy = 1;

g_thread_workers[i].sockfd = sockfd;

if (pthread_cond_signal(&g_cond) != 0) // 唤醒一个线程

{

perror("Failed to signal condition");

}

return 0;

}

}

// all threads are busy

return -1;

}

int remove_worker(pthread_t tid)

{

int i;

for (i = 0; i < kMaxWorkerNum; ++i)

{

if (g_thread_workers[i].thread == tid)

{

g_thread_workers[i].thread = 0;

g_thread_workers[i].busy = 0;

g_thread_workers[i].sockfd = -1;

return 0;

}

}

// no such thread

return -1;

}

int start_worker_threads(void)

{

int i;

g_thread_workers = (thread_worker *)calloc(kMaxWorkerNum, sizeof(thread_worker));

if (g_thread_workers == NULL)

{

perror("Failed to allocate memory");

return -1;

}

for (i = 0; i < kMaxWorkerNum; ++i)

{

if (pthread_create(&g_thread_workers[i].thread, NULL, thread_func, NULL) != 0)

{

perror("Failed to create thread");

return -1;

}

}

return 0;

}

void stop_worker_threads(void)

{

int i;

for (i = 0; i < kMaxWorkerNum; ++i)

{

pthread_cancel(g_thread_workers[i].thread);

pthread_join(g_thread_workers[i].thread, NULL);

}

free(g_thread_workers);

}

int main(int argc, char *argv[])

{

int sockfd;

if (start_worker_threads() != 0)

{

printf("Failed to start worker threads\n");

return 1;

}

while (1)

{

sockfd = accept(...); // 接收连接

if (sockfd != -1)

{

add_worker(sockfd);

}

}

stop_worker_threads();

return 0;

}

```

这个例子中创建了一个最大容纳8个线程的线程池,线程会执行thread_func() 函数。在主线程中,使用accept()函数等待客户端连接,并将连接的socket传递给空闲线程池处理,即调用add_worker() 函数。线程在处理完任务后,通知主线程已经处理完成,并进入缓冲队列等待下一个任务,即调用pthread_cond_wait() 函数,并把is_busy设置为0。

在主线程中,add_worker() 函数在找到空闲线程后,会将socket存储到线程的is_busy和sockfd变量中,并唤醒其中一个线程池线程去处理。remove_worker() 函数和start_worker_threads() 函数的实现是删除和创建线程的逆操作。当所有线程处理完已有任务后,主线程执行stop_worker_threads() 函数停止线程池中的所有线程,关闭socket并释放资源。

二、Linux多线程编程的应用

除了上述介绍的线程池,Linux下的多线程编程也有许多应用场景。下面是一些常见的应用场景以及使用多线程编程的例子。

1. 多线程服务器

多线程服务器指的是在一个服务器中处理多个请求。传统的多进程服务器在每个连接中都创建了进程来处理,如果同时连接的客户端数量很多,需要创建大量进程,极容易导致系统负担过重,从而导致服务器宕机。

多线程服务器对我们很有帮助,它可以优化服务器并发度问题,提高处理请求效率。在多线程服务器中,我们可以使用线程池技术和多线程技术。多线程服务器在创建一个服务器线程、监听并接收用户请求后,将请求任务封装成一个任务队列,由线程池中的线程依次处理,使得单个线程可以同时处理多个请求,这样大大提高了服务器的并发度。下面是一个基本的多线程服务器实现:

```

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define PORT 8080

#define BACKLOG 32

#define MAX_CLIENTS 64

typedef struct {

pthread_t thread; // 线程ID

int sockfd; // socket文件描述符

} client_info;

static int g_listenfd; // 监听socket文件描述符

static int g_stop = 0; // 是否停止服务器

static int g_num_clients = 0; // 当前连接数量

static client_info g_clients[MAX_CLIENTS]; // 客户端信息结构体数组

static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;

void *handle_client(void *arg)

{

char buf[512];

int clientfd = *(int *)arg;

int nread;

while ((nread = read(clientfd, buf, 512)) > 0)

{

if (write(clientfd, buf, nread) == -1)

{

perror("Failed to write to socket");

break;

}

}

close(clientfd);

pthread_mutex_lock(&g_mutex);

int i;

for (i = 0; i < g_num_clients; ++i)

{

if (clientfd == g_clients[i].sockfd)

{

printf("Client %d disconnected\n", clientfd);

g_clients[i].sockfd = -1;

g_clients[i].thread = 0;

--g_num_clients;

break;

}

}

pthread_mutex_unlock(&g_mutex);

return NULL;

}

void *accept_clients(void *arg)

{

int clientfd;

int i;

while (!g_stop)

{

clientfd = accept(g_listenfd, NULL, NULL);

if (clientfd == -1)

{

perror("Failed to accept connection");

continue;

}

pthread_mutex_lock(&g_mutex);

if (g_num_clients < MAX_CLIENTS)

{

for (i = 0; i < MAX_CLIENTS; ++i)

{

if (g_clients[i].sockfd == -1)

{

g_clients[i].sockfd = clientfd;

if (pthread_create(&g_clients[i].thread, NULL,

handle_client, &g_clients[i].sockfd) != 0)

{

perror("Failed to create thread");

g_clients[i].sockfd = -1;

}

else

{

++g_num_clients;

printf("Client %d connected\n", clientfd);

}

break;

}

}

}

if (i == MAX_CLIENTS)

{

printf("Too many clients, closing the connection\n");

close(clientfd);

}

pthread_mutex_unlock(&g_mutex);

}

return NULL;

}

int start_server(void)

{

int ret;

struct sockaddr_in server_addr;

g_listenfd = socket(AF_INET, SOCK_STREAM, 0);

if (g_listenfd == -1)

{

perror("Failed to create socket");

return -1;

}

memset(&server_addr, 0, sizeof(server_addr));

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(PORT);

server_addr.sin_addr.s_addr = INADDR_ANY;

ret = bind(g_listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

if (ret == -1)

{

perror("Failed to bind address");

return -1;

}

ret = listen(g_listenfd, BACKLOG);

if (ret == -1)

{

perror("Failed to listen on socket");

return -1;

}

return 0;

}

void stop_server(void)

{

close(g_listenfd);

g_stop = 1;

}

int main(int argc, char *argv[])

{

if (start_server() != 0)

{

printf("Failed to start server\n");

return 1;

}

pthread_t accept_tid;

pthread_create(&accept_tid, NULL, accept_clients, NULL);

getchar();

stop_server();

pthread_join(accept_tid, NULL);

pthread_mutex_lock(&g_mutex);

int i;

for (i = 0; i < MAX_CLIENTS; ++i)

{

if (g_clients[i].sockfd != -1)

{

close(g_clients[i].sockfd);

pthread_join(g_clients[i].thread, NULL);

}

}

pthread_mutex_unlock(&g_mutex);

return 0;

}

```

这个例子中创建了一个多线程服务器,服务器启动时会创建一个监听socket,并接收客户端连接,将客户端socket文件描述符存储到客户端信息结构体数组g_clients中。在调用accept_clients()函数时,会创建一个线程来监听客户端连接,等待客户端请求。如果有客户端连接,将客户端socket文件描述符传递到空闲线程中处理,并将该socket存储到客户端信息结构体数组中。当客户端关闭连接后,将客户端信息结构体数组中的对应文件描述符设置为-1,并在客户端信息结构体数组中删除该元素。

2. 多线程排序

多线程排序在大数据排列、并行计算等方面

  • 原标题:掌握Linux多线程编程技能,提高程序效率

  • 本文链接:https://qipaikaifa1.com/jsbk/15944.html

  • 本文由沈阳淘贝游戏开发公司小编,整理排版发布,转载请注明出处。部分文章图片来源于网络,如有侵权,请与淘贝科技联系删除。
  • 微信二维码

    CTAPP999

    长按复制微信号,添加好友

    微信联系

    在线咨询

    点击这里给我发消息QQ客服专员


    点击这里给我发消息电话客服专员


    在线咨询

    免费通话


    24h咨询☎️:189-2934-0276


    🔺🔺 棋牌游戏开发24H咨询电话 🔺🔺

    免费通话
    返回顶部