学习Linux线程:深入理解,轻松上手,详解实例 (linux线程 例子)
随着计算机技术的不断发展,Linux作为一种开源的操作系统,得到了众多开发者和计算机爱好者的青睐。在Linux系统中,线程作为一种重要的机制,在许多应用程序中得到广泛的应用。
学习Linux线程,相信是每一位程序员成长过程中的必经之路。本文将从深入理解线程的概念、轻松上手线程的使用以及详解实例等方面,为大家呈现一份全面的Linux线程学习笔记。
一、深入理解线程的概念
线程是一个轻量级的进程,它是程序中执行的最小单元,是处理器调度和分派的基本单位。而进程是资源分配和调度的基本单位。一个进程包括一个或多个线程,这些线程共享进程资源,但每个线程又拥有自己独立的栈空间、程序计数器和线程上下文等。
在Linux系统中,每个线程都有一个线程ID(TID)和一个可能独立的堆栈,但是它们共享其它的资源,如进程ID、代码段、数据段、打开的文件和信号处理函数等。
线程的优点在于它可以减少进程之间的上下文切换开销,同时它也可以有效利用系统资源。但是线程也存在一些缺点,如同步、互斥的问题。因此,程序员需要根据具体情况选择进程和线程。
二、轻松上手线程的使用
为了更好地理解线程的使用,我们需要了解一些Linux系统提供的线程API。下面将介绍一些常用的线程API和其基本用法:
1. pthread_create()
pthread_create()函数是Linux系统中创建线程的最基本函数之一。该函数的原型如下所示:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
其中,pthread_t类型是一个线程ID。pthread_attr_t类型是用来指定线程属性的参数,如果这个参数为NULL,则线程将使用默认属性。
start_routine是一个指向函数的指针,该函数是线程的入口点。arg是传入start_routine函数的参数。
下面是一个使用pthread_create()函数创建线程的实例代码:
#include
#include
void *task(void *arg)
{
printf(“Thread is running …\n”);
return NULL;
}
int mn()
{
pthread_t tid;
pthread_create(&tid, NULL, task, NULL);
printf(“Thead has been created.\n”);
pthread_join(tid, NULL);
printf(“Thead has been finished.\n”);
return 0;
}
在这个例子中,我们使用pthread_create()函数创建了一个名为“task”的线程,并在该线程中输出了一个字符串。在主线程中,我们使用pthread_join()函数来等待“task”线程的结束。
2. pthread_mutex_t
pthread_mutex_t类型是Linux系统中用于线程同步的一种互斥锁,它是多线程编程中非常重要的一个概念。下面是这个类型的定义:
typedef struct {
volatile int __lock;
} pthread_mutex_t;
其中,__lock变量是用来表示锁的状态的,它的值为0时表示锁是未锁定状态,值为1时表示锁已锁定状态。
以下是一个使用pthread_mutex_t实现线程同步的示例:
#include
#include
pthread_mutex_t lock;
int count = 0;
void *task(void *arg)
{
pthread_mutex_lock(&lock);
printf(“Thread is running, count = %d\n”, count);
count++;
pthread_mutex_unlock(&lock);
return NULL;
}
int mn()
{
int i;
pthread_t tids[10];
pthread_mutex_init(&lock, NULL);
for (i = 0; i
pthread_create(&tids[i], NULL, task, NULL);
}
for (i = 0; i
pthread_join(tids[i], NULL);
}
pthread_mutex_destroy(&lock);
return 0;
}
在这个例子中,我们使用pthread_mutex_t来保证多个线程能够正确地对count变量进行操作。当一个线程使用pthread_mutex_lock()函数来锁定锁时,如果锁已经被其它线程锁定了,则当前线程会被阻塞,直到其它线程释放了锁。当当前线程执行完操作时,使用pthread_mutex_unlock()函数来释放锁。
三、详解实例
下面将通过一个实际应用,深入地了解Linux线程的使用方法。我们将使用线程来构建一个多线程FTP服务器,供客户端上传和下载文件。
1. 服务器端
服务器端的主要工作是使用socket接口来监听客户端的请求,在客户端的请求到来时,创建一个新的线程来处理客户端的请求。
下面是服务器端的实例代码:
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8888
#define MAX_LINE 2023
void *client_handler(void *);
int mn()
{
int sock_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len;
pthread_t tid;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) {
perror(“socket”);
exit(-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 = htonl(INADDR_ANY);
if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror(“bind”);
exit(-1);
}
if (listen(sock_fd, 5) == -1) {
perror(“listen”);
exit(-1);
}
client_len = sizeof(client_addr);
while (1) {
client_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
perror(“accept”);
break;
}
if (pthread_create(&tid, NULL, client_handler, (void *)client_fd) != 0) {
perror(“pthread_create”);
break;
}
}
close(sock_fd);
return 0;
}
在这个例子中,我们使用socket接口来创建一个TCP套接字,然后绑定到指定的端口上,并使用listen()函数来进行监听。在接受客户端的连接请求时,我们创建了一个新的线程来处理客户端请求,这个线程的入口点是client_handler()函数。
2. 客户端
客户端主要的工作是向服务器发送上传和下载请求,并接收服务器返回的数据。
以下是客户端的实例代码:
#include
#include
#include
#include
#include
#include
#include
#define PORT 8888
#define MAX_LINE 2023
char server_ip[] = “127.0.0.1”;
void download_file(int sockfd, char *filename)
{
char buffer[MAX_LINE];
FILE *fp;
int len;
sprintf(buffer, “get %s”, filename);
if (send(sockfd, buffer, strlen(buffer), 0) == -1) {
perror(“send”);
exit(-1);
}
fp = fopen(filename, “wb”);
if (!fp) {
perror(“fopen”);
exit(-1);
}
while ((len = recv(sockfd, buffer, MAX_LINE, 0)) > 0) {
fwrite(buffer, 1, len, fp);
}
fclose(fp);
}
void upload_file(int sockfd, char *filename)
{
char buffer[MAX_LINE];
FILE *fp;
int len;
sprintf(buffer, “put %s”, filename);
if (send(sockfd, buffer, strlen(buffer), 0) == -1) {
perror(“send”);
exit(-1);
}
fp = fopen(filename, “rb”);
if (!fp) {
perror(“fopen”);
exit(-1);
}
while ((len = fread(buffer, 1, MAX_LINE, fp)) > 0) {
if (send(sockfd, buffer, len, 0) == -1) {
perror(“send”);
exit(-1);
}
}
fclose(fp);
}
int mn()
{
int sockfd;
struct sockaddr_in server_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror(“socket”);
exit(-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 = inet_addr(server_ip);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror(“connect”);
exit(-1);
}
upload_file(sockfd, “test.txt”);
download_file(sockfd, “test.txt”);
close(sockfd);
return 0;
}
在这个例子中,我们使用socket接口来创建一个TCP套接字,并使用connect()函数来连接服务器。在向服务器发送上传请求和下载请求时,我们使用send()函数发送数据,接收数据时使用recv()函数。