当前位置: 首页 > wzjs >正文

网站的英文版怎么做的百度竞价怎么开户

网站的英文版怎么做的,百度竞价怎么开户,常见的网站开发软件有哪些,淘宝客推广怎样做网站在计算机编程的广阔领域中,POSIX 标准就像是一把通用的钥匙,开启了跨平台编程的大门。POSIX,即 Portable Operating System Interface(可移植操作系统接口) ,是 IEEE 为了规范各种 UNIX 操作系统提供的 API…

在计算机编程的广阔领域中,POSIX 标准就像是一把通用的钥匙,开启了跨平台编程的大门。POSIX,即 Portable Operating System Interface(可移植操作系统接口) ,是 IEEE 为了规范各种 UNIX 操作系统提供的 API 接口而定义的一系列互相关联标准的总称。它的出现,旨在解决不同 UNIX 系统之间接口不一致的问题,让开发者能够编写一次代码,在多个符合 POSIX 标准的系统上运行,实现源代码级别的软件可移植性。

对于多线程编程而言,POSIX 标准同样意义非凡。在多核处理器盛行的今天,多线程编程成为充分利用硬件资源、提高程序性能的关键技术。POSIX 标准定义了一套清晰、规范的多线程编程接口,让开发者可以在不同的操作系统环境中,以统一的方式创建、管理线程,以及处理线程之间的同步和通信问题 。无论是开发高性能的服务器程序,还是优化计算密集型的应用,POSIX 标准下的多线程编程都能提供强大的支持。

接下来,让我们深入探索 POSIX 标准下的多线程编程世界,揭开线程创建、同步机制等核心概念的神秘面纱。

一、多线程编程简介

1.1线程初印象

线程,作为进程内的执行单元,可以理解为进程这个大舞台上的一个个小舞者,各自有着独立的舞步(执行路径),却又共享着舞台的资源(进程资源)。与进程相比,线程更加轻量级。进程是系统进行资源分配和调度的基本单位,拥有独立的地址空间、内存、文件描述符等资源 ,进程间的切换开销较大。而线程则是共享所属进程的资源,它们之间的切换开销相对较小,就像在同一个舞台上不同舞者之间的快速换位,无需重新搭建整个舞台。

线程的这些特点,使得多线程编程在提升程序执行效率上有着独特的优势。多个线程可以并发执行,充分利用多核处理器的并行计算能力,将复杂的任务分解为多个子任务,每个子任务由一个线程负责处理,从而大大提高了程序的整体运行速度。例如,在一个网络服务器程序中,一个线程可以负责监听客户端的连接请求,另一个线程负责处理已经建立连接的客户端的数据传输,这样可以同时处理多个客户端的请求,提升服务器的响应性能 。

1.2POSIX 线程库

在 POSIX 标准下,进行多线程编程离不开 POSIX 线程库(pthread 库)。它就像是一根神奇的魔法棒,为开发者提供了一系列强大的接口函数,让我们能够轻松地操控线程。

其中,pthread_create函数用于创建一个新的线程 ,它的原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

thread参数用于返回新创建线程的 ID;attr参数用于设置线程的属性,如果为NULL则使用默认属性;start_routine是一个函数指针,指向线程开始执行时调用的函数;arg是传递给start_routine函数的参数。

而pthread_join函数则用于等待一个线程结束,其原型为:

int pthread_join(pthread_t thread, void **retval);

thread参数是要等待结束的线程 ID,retval用于获取线程结束时的返回值。

下面是一个简单的使用pthread_create和pthread_join函数的代码示例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>// 线程执行的函数
void* thread_function(void* arg) {printf("线程开始执行,参数为: %s\n", (char*)arg);sleep(2);  // 模拟线程执行任务printf("线程执行结束\n");return (void*)1;  // 返回线程执行结果
}int main() {pthread_t thread;int res;void* thread_result;// 创建线程res = pthread_create(&thread, NULL, thread_function, (void*)"Hello, Thread!");if (res != 0) {perror("线程创建失败");return 1;}printf("等待线程结束...\n");// 等待线程结束,并获取线程返回值res = pthread_join(thread, &thread_result);if (res != 0) {perror("线程等待失败");return 1;}printf("线程已结束,返回值为: %ld\n", (long)thread_result);return 0;
}

在这个示例中,我们创建了一个新线程,线程执行thread_function函数,在函数中打印传入的参数,然后休眠 2 秒模拟执行任务,最后返回一个值。主线程通过pthread_join等待子线程结束,并获取其返回值。

1.3线程的生命周期

线程如同一个有生命的个体,有着自己完整的生命周期,从创建的那一刻开始,经历运行、阻塞、唤醒等阶段,最终走向结束。

当我们调用pthread_create函数时,线程就诞生了,此时它处于就绪状态,等待着 CPU 的调度。一旦获得 CPU 时间片,线程就进入运行状态,开始执行它的任务,也就是调用我们指定的函数 。

在运行过程中,线程可能会因为某些原因进入阻塞状态。比如,当线程调用sleep函数时,它会主动放弃 CPU 使用权,进入睡眠状态,直到睡眠时间结束才会重新回到就绪状态,等待再次被调度执行 。又或者,当线程访问共享资源时,如果资源被其他线程占用,它就需要等待,从而进入阻塞状态,直到获取到资源才会被唤醒,重新进入运行状态。

当线程执行完它的任务,也就是指定的函数返回时,线程就进入了结束状态。此时,我们可以通过pthread_join函数等待线程结束,并获取它的返回值 ,也可以在创建线程时将其设置为分离状态,这样线程结束后资源会自动被回收,无需等待。了解线程的生命周期,有助于我们更好地管理线程,优化程序的性能 。

二、Posix网络API

2.1客户端和服务端代码示例

(1)服务端server.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char *argv[])
{if (argc != 2){printf("Using:./server port\nExample:./server 5005\n\n"); return -1;}// 第1步:创建服务端的socket。int listenfd;if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); return -1; }// 第2步:把服务端用于通信的地址和端口绑定到socket上。struct sockaddr_in servaddr;    // 服务端地址信息的数据结构。memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;  // 协议族,在socket编程中只能是AF_INET。servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // 任意ip地址。//servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); // 指定ip地址。servaddr.sin_port = htons(atoi(argv[1]));  // 指定通信端口。if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 ){ perror("bind"); close(listenfd); return -1; }// 第3步:把socket设置为监听模式。if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }// 第4步:接受客户端的连接。int  clientfd;                  // 连上来的客户端socket。int  socklen = sizeof(struct sockaddr_in); // struct sockaddr_in的大小struct sockaddr_in clientaddr;  // 客户端的地址信息。clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t*)&socklen);printf("client (%s) connect server success。。。\n", inet_ntoa(clientaddr.sin_addr));// 第5步:与客户端通信,接收客户端发过来的报文后,将该报文原封不动返回给客户端。char buffer[1024];// memset(buffer, 0, 1024);while (1){int ret;memset(buffer, 0, sizeof(buffer));// 接收客户端的请求报文。if ( (ret = recv(clientfd, buffer, sizeof(buffer), 0)) <= 0) {printf("ret = %d , client disconected!!!\n", ret); break;   }printf("recv msg: %s\n", buffer);// 向客户端发送响应结果。if ( (ret = send(clientfd, buffer, strlen(buffer), 0)) <= 0) { perror("send"); break; }printf("response client: %s success...\n", buffer);}// 第6步:关闭socket,释放资源。close(listenfd); close(clientfd); return 0;
}

(2)客户端client.cpp

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char *argv[])
{if (argc != 3){printf("Using:./client ip port\nExample:./client 127.0.0.1 5005\n\n"); return -1;}// 第1步:创建客户端的socket。int sockfd;if ( (sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }// 第2步:向服务器发起连接请求。struct hostent* h;if ( (h = gethostbyname(argv[1])) == 0 )   // 指定服务端的ip地址。{ printf("gethostbyname failed.\n"); close(sockfd); return -1; }struct sockaddr_in servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2])); // 指定服务端的通信端口。memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);// 向服务端发起连接清求。if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)  { perror("connect"); close(sockfd); return -1; }char buffer[1024];// 第3步:与服务端通信,发送一个报文后等待回复,然后再发下一个报文。for (int i = 0; i < 3; i++){int ret;memset(buffer, 0, sizeof(buffer));sprintf(buffer, "这是第[%d]条消息!", i+1);if ( (ret = send(sockfd, buffer, strlen(buffer),0)) <= 0) // 向服务端发送请求报文。{ perror("send"); break; }printf("发送:%s\n", buffer);memset(buffer,0,sizeof(buffer));if ( (ret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) // 接收服务端的回应报文。{printf("ret = %d error\n", ret); break;}printf("从服务端接收:%s\n", buffer);sleep(1);}// 第4步:关闭socket,释放资源。close(sockfd);
}

着重分析以下几个函数

(1)socket函数

int socket(int domain, int type, int protocol);

调用socket()函数会创建一个套接字(socket)对象。套接字由两部分组成,文件描述符(fd)和 TCP控制块(Tcp Control Block,tcb) 。Tcb主要包括关系信息有网络的五元组(remote IP,remote Port, local IP, local Port, protocol),一个五元组就可以确定一个具体的网络连接。

(2)listen函数

listen(int listenfd, backlog);

服务端在调用listen()后,就开始监听网络上连接请求。第二个参数 backlog, 在Linux是指全连接队列的长度,即一次最多能保存 backlog 个连接请求。

(3)connect 函数

客户端调用connect()函数,向指定服务端发起连接请求。

(4)accept 函数

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept()函数只做两件事,将连接请求从全连接队列中取出,给该连接分配一个fd并返回。

(5) 三次握手过程分析

三次握手与listen/connect/accept三个函数有关,这里放到一起进行描述。

客户端调用 connect 函数,开始进入三次握手。客户端发送syn包,以及带着随机的seq;

服务端listen函数监听到有客户端连接,listen函数会在内核协议栈为该客户端创建一个Tcb控制块,并将其加入到半连接队列。服务端在收到syn包后,会给客户端恢复ack和syn包;

客户端收到服务端的ack和syn后再次恢复ack,连接建立成功。

服务端在收到客户端的ack后,会将该客户端对应的Tcb数据从半连接队列移动到全连接队列。只要全连接队列中有数据就会触发accept,返回连接成功的客户端fd、IP以及端口。此时,Tcb完整的五元组构建成功。

(6)send/recv 函数

至此,客户端与服务端已经成功建立连接,就可以相互通信了。

send/recv函数主要负责数据的收发。

过程分析

send函数:负责将数据从用户空间拷贝到内核(具体是拷贝到该连接对应的Tcb控制块中的发送缓冲区)。注意:send函数返回并不意味着数据已成功发送,因为数据在到达内核缓冲区后,内核会根据自己的策略决定什么时候将数据发出。

recv函数:负责将数据从内核缓冲区拷贝到用户空间。同理,数据也显示到达该连接对应的Tcb控制块的接受缓冲区。

(7)close 函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成读写操作后我们需要关闭相应的socket,好比操作完打开的文件要调用fclose关闭打开的文件一样。close过程涉及到四次挥手的全过程

四次挥手流程:

  • 客户端调用close函数,内核会发送fin包,客户端进入fin_wait1状态;

  • 服务端收到fin包回复ack,客户端进入close_wait状态。此时,客户客户端往服务端发送的通道就关闭了,因为Tcp是全双工的,服务端还可以向客户端发数据。

  • 客户端收到ack,进入到fin_wait2状态;

  • 服务端发送完数据,发送fin包,服务端进入last_ack状态;

  • 客户端收到fin包后,回复ack,进入到time_wait状态;

  • 服务端收到ack,双方连接正常关闭。

注意:close操作只是让相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求

2.2常见面试问题

为什么要三次握手?

答:因为一个完整的TCP连接需要双方都得到确认,客户端发送请求和收到确认需要两次;服务端发送请求和收到确认需要两次,当中服务回复确认和发送请求合并为一次总共需要3次;才能保证双向通道是通的。

一个服务器的端口数是65535,为何能做到一百万的连接?

答:主要是因为一条连接是由五元组所组成,所以一个服务器的连接数是五个成员数的乘积。

如何应对Dos(Deny of Service,拒绝服务)攻击?

答:Dos攻击就是利用三次握手的原理,模拟客户端只向服务器发送syn包,然后耗尽被攻击对象的资源。比较多的做法是利用防火墙,做一些过滤规则

如何解决Tcp的粘包问题?

答:(1) 在包头上添加一个数据包长度的字段,用于数据的划分,实际项目中这个也用的最多;(2)包尾部加固定分隔符;

Tcp如何保证顺序到达?

答:顺序到达是由于TCP的延迟ACK的机制来保证的,TCP接收到数据并不是立即回复而是经过一个延迟时间,回复接收到连续包的最大序列号加1。如果丢包之后的包都需要重传。在弱网情况下这里就会有实时性问题和带宽占用的问题;

time_wait 作用?

答:防止最后一个ACK没有顺利到达对方,超时重新发送ack。time_wait时常一般是120s可以修改。

服务器掉线重启出现端口被占用怎么办?

答:其实主要是由于还处于time_wait状态,端口并没有真正释放。这时候可以设置SO_REUSEADDR属性,保证掉线能马上重连。

三、同步机制:多线程协作的 “指挥家”

在多线程编程的舞台上,同步机制就像是一位经验丰富的指挥家,协调着各个线程的行动,确保它们能够和谐共处,高效地完成任务。多线程编程中,由于多个线程共享进程资源,资源竞争和线程协作问题不可避免,而同步机制正是解决这些问题的关键。接下来,我们将深入探讨互斥锁、信号量和条件变量这几种常见的同步机制 。

3.1资源竞争:多线程中的 “暗礁”

当多个线程同时访问和修改共享资源时,资源竞争问题就如同隐藏在暗处的暗礁,随时可能让程序的运行陷入混乱。假设我们有一个简单的程序,包含两个线程,它们都试图对一个全局变量进行加 1 操作:

#include <stdio.h>
#include <pthread.h>// 全局变量
int global_variable = 0;// 线程执行函数
void* thread_function(void* arg) {for (int i = 0; i < 1000000; i++) {global_variable++;}return NULL;
}int main() {pthread_t thread1, thread2;// 创建线程pthread_create(&thread1, NULL, thread_function, NULL);pthread_create(&thread2, NULL, thread_function, NULL);// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);printf("最终的全局变量值: %d\n", global_variable);return 0;
}

按照我们的预期,两个线程各对全局变量加 1000000 次,最终的结果应该是 2000000。然而,实际运行这个程序,你会发现结果往往小于 2000000。这是因为在多线程环境下,global_variable++ 这一操作并非原子操作,它实际上包含了读取变量值、加 1、写回变量值这三个步骤 。当两个线程同时执行这一操作时,可能会出现一个线程读取了变量值,还未完成加 1 和写回操作,另一个线程也读取了相同的值,导致最终结果出现偏差,数据不一致 。

3.2互斥锁:守护资源的 “卫士”

互斥锁(Mutex)是解决资源竞争问题的常用工具,它就像一位忠诚的卫士,守护着共享资源,确保同一时间只有一个线程能够访问资源。互斥锁的工作原理基于一个简单的概念:当一个线程获取到互斥锁时,其他线程就必须等待,直到该线程释放互斥锁。

在 POSIX 线程库中,使用互斥锁非常简单。首先,我们需要定义一个互斥锁变量:

pthread_mutex_t mutex;

然后,在访问共享资源之前,通过 pthread_mutex_lock 函数获取互斥锁:

pthread_mutex_lock(&mutex);

如果互斥锁已经被其他线程持有,调用 pthread_mutex_lock 的线程将被阻塞,直到互斥锁被释放。当访问完共享资源后,使用 pthread_mutex_unlock 函数释放互斥锁:

pthread_mutex_unlock(&mutex);

下面是使用互斥锁改进后的代码:

#include <stdio.h>
#include <pthread.h>// 全局变量
int global_variable = 0;
// 互斥锁
pthread_mutex_t mutex;// 线程执行函数
void* thread_function(void* arg) {for (int i = 0; i < 1000000; i++) {// 获取互斥锁pthread_mutex_lock(&mutex);global_variable++;// 释放互斥锁pthread_mutex_unlock(&mutex);}return NULL;
}int main() {pthread_t thread1, thread2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建线程pthread_create(&thread1, NULL, thread_function, NULL);pthread_create(&thread2, NULL, thread_function, NULL);// 等待线程结束pthread_join(thread1, NULL);pthread_join(thread2, NULL);// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("最终的全局变量值: %d\n", global_variable);return 0;
}

通过这种方式,互斥锁有效地保护了共享资源,确保了数据的一致性 。

3.3信号量:资源分配的 “调度员”

信号量(Semaphore)是另一种强大的同步工具,它不仅可以用于实现互斥,还能用于管理资源的分配。信号量可以看作是一个计数器,它的值表示可用资源的数量 。当一个线程想要访问资源时,它需要先获取信号量,如果信号量的值大于 0,则表示有可用资源,线程可以获取信号量并继续执行,同时信号量的值减 1;如果信号量的值为 0,则表示没有可用资源,线程将被阻塞,直到有其他线程释放信号量 。

在 POSIX 标准中,信号量相关的函数主要有 sem_init(初始化信号量)、sem_wait(等待信号量)、sem_post(释放信号量)和 sem_destroy(销毁信号量)。假设我们有一个场景,有多个线程需要访问有限数量的资源,比如数据库连接池中的连接。我们可以使用信号量来控制对这些资源的访问:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>// 定义信号量,假设有5个可用资源
sem_t semaphore;// 线程执行函数
void* thread_function(void* arg) {// 等待信号量sem_wait(&semaphore);printf("线程获取到资源,开始执行任务...\n");// 模拟任务执行sleep(1);printf("线程任务执行完毕,释放资源\n");// 释放信号量sem_post(&semaphore);return NULL;
}int main() {pthread_t threads[10];// 初始化信号量,设置初始值为5sem_init(&semaphore, 0, 5);// 创建10个线程for (int i = 0; i < 10; i++) {pthread_create(&threads[i], NULL, thread_function, NULL);}// 等待所有线程结束for (int i = 0; i < 10; i++) {pthread_join(threads[i], NULL);}// 销毁信号量sem_destroy(&semaphore);return 0;
}

在这个例子中,我们初始化信号量的值为 5,表示有 5 个可用资源。每个线程在执行任务前先通过 sem_wait 等待信号量,获取到信号量后才能访问资源,执行完任务后通过 sem_post 释放信号量,这样就保证了同时最多只有 5 个线程可以访问资源 。

3.4条件变量:线程间的 “传声筒”

条件变量(Condition Variable)用于线程间基于条件的通信,它为线程提供了一种等待特定条件发生的机制,就像一个传声筒,让线程之间能够相互传达信息。条件变量通常与互斥锁配合使用,以实现线程之间的同步和协作。

一个经典的例子是生产者 - 消费者模型。在这个模型中,生产者线程负责生成数据并将其放入缓冲区,消费者线程则从缓冲区中取出数据进行处理。当缓冲区为空时,消费者线程需要等待,直到生产者线程向缓冲区中放入数据;当缓冲区满时,生产者线程需要等待,直到消费者线程从缓冲区中取出数据 。

下面是使用条件变量和互斥锁实现生产者 - 消费者模型的代码示例:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0, out = 0;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;// 生产者线程函数
void* producer(void* arg) {while (1) {int item = rand() % 100; // 生成一个随机数pthread_mutex_lock(&mutex);while ((in + 1) % BUFFER_SIZE == out) { // 缓冲区满pthread_cond_wait(&not_full, &mutex);}buffer[in] = item;printf("生产者放入数据: %d\n", item);in = (in + 1) % BUFFER_SIZE;pthread_cond_signal(&not_empty);pthread_mutex_unlock(&mutex);sleep(rand() % 2); // 模拟生产时间}return NULL;
}// 消费者线程函数
void* consumer(void* arg) {while (1) {pthread_mutex_lock(&mutex);while (in == out) { // 缓冲区空pthread_cond_wait(&not_empty, &mutex);}int item = buffer[out];printf("消费者取出数据: %d\n", item);out = (out + 1) % BUFFER_SIZE;pthread_cond_signal(&not_full);pthread_mutex_unlock(&mutex);sleep(rand() % 3); // 模拟消费时间}return NULL;
}int main() {pthread_t producer_thread, consumer_thread;// 创建生产者和消费者线程pthread_create(&producer_thread, NULL, producer, NULL);pthread_create(&consumer_thread, NULL, consumer, NULL);// 等待线程结束pthread_join(producer_thread, NULL);pthread_join(consumer_thread, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(&not_empty);pthread_cond_destroy(&not_full);return 0;
}

在这个代码中,pthread_cond_wait 函数会使线程进入等待状态,并自动释放互斥锁,当条件满足被唤醒时,会重新获取互斥锁。pthread_cond_signal 函数则用于唤醒等待在条件变量上的一个线程。通过条件变量和互斥锁的紧密配合,生产者和消费者线程能够有条不紊地工作,实现高效的数据处理 。

四、多线程编程实战演练

4.1多线程案例分析

在日常的编程工作中,文件处理是一项常见的任务。当面对大量文件需要处理时,单线程的处理方式往往效率低下,而多线程编程则能成为提升效率的利器。假设我们有一个需求:处理一批日志文件,需要统计每个文件中特定关键词出现的次数,并将结果汇总。

为了实现这个目标,我们可以设计一个多线程的文件处理方案。首先,将文件列表进行分割,把不同的文件分配给不同的线程处理,这就像是将一堆任务分配给不同的工人,每个工人专注于自己手头的任务 。每个线程负责读取分配给自己的文件内容,逐行扫描,统计关键词出现的次数。

这个过程中,线程之间的同步机制至关重要。我们可以使用互斥锁来保护共享的统计结果变量,确保不同线程在更新统计结果时不会出现数据竞争问题 。比如,当一个线程统计完自己负责文件后,需要将统计结果累加到全局的统计变量中,此时通过获取互斥锁,保证同一时间只有一个线程能够进行累加操作,避免了数据不一致的情况 。

4.2代码实现示例

下面是使用 POSIX 线程库实现多线程文件处理的具体代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>#define MAX_FILES 10
#define KEYWORD "error"  // 要统计的关键词// 线程参数结构体
typedef struct {char *file_name;
} ThreadArgs;// 全局统计变量
int global_count = 0;
// 互斥锁
pthread_mutex_t mutex;// 线程执行函数
void* count_keyword(void* arg) {ThreadArgs *args = (ThreadArgs*)arg;FILE *file = fopen(args->file_name, "r");if (file == NULL) {perror("文件打开失败");pthread_exit(NULL);}char line[1024];int local_count = 0;while (fgets(line, sizeof(line), file) != NULL) {if (strstr(line, KEYWORD) != NULL) {local_count++;}}fclose(file);// 获取互斥锁,更新全局统计变量pthread_mutex_lock(&mutex);global_count += local_count;pthread_mutex_unlock(&mutex);pthread_exit(NULL);
}int main() {pthread_t threads[MAX_FILES];ThreadArgs args[MAX_FILES];char file_names[MAX_FILES][50] = {"file1.log", "file2.log", "file3.log", "file4.log", "file5.log", "file6.log", "file7.log", "file8.log", "file9.log", "file10.log"};// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建线程并分配文件for (int i = 0; i < MAX_FILES; i++) {args[i].file_name = file_names[i];if (pthread_create(&threads[i], NULL, count_keyword, &args[i]) != 0) {perror("线程创建失败");return 1;}}// 等待所有线程结束for (int i = 0; i < MAX_FILES; i++) {if (pthread_join(threads[i], NULL) != 0) {perror("线程等待失败");return 1;}}// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("关键词 '%s' 出现的总次数: %d\n", KEYWORD, global_count);return 0;
}

在这段代码中,count_keyword 函数是线程执行的主体,它打开分配的文件,逐行读取并统计关键词出现的次数,最后通过互斥锁将本地统计结果累加到全局变量中 。main 函数负责创建线程,为每个线程分配文件,并等待所有线程执行完毕后输出最终的统计结果 。

4.3多线程调试与优化

在多线程程序的调试过程中,我们可能会遇到各种各样的问题。死锁是一个常见的问题,比如两个线程分别持有不同的锁,却又试图获取对方持有的锁,就会陷入死锁状态,导致程序无法继续执行 。为了检测死锁,可以使用工具如Valgrind的Helgrind工具,它能够帮助我们发现潜在的死锁问题。一旦发现死锁,我们需要仔细检查代码中锁的获取和释放顺序,避免嵌套锁的不合理使用 。

线程异常也是需要关注的问题。当线程执行过程中出现未捕获的异常时,可能会导致整个程序崩溃。我们可以在线程函数中使用try - catch块(如果是 C++ 代码)或者进行适当的错误处理,确保线程在遇到异常时能够安全地退出,而不影响其他线程的正常运行 。

在优化方面,合理调整线程数量是一个重要的思路。线程数量并非越多越好,过多的线程会导致上下文切换开销增大,反而降低程序性能 。对于 CPU 密集型的任务,线程数量可以设置为接近 CPU 核心数;对于 I/O 密集型的任务,由于线程在等待 I/O 操作时会阻塞,不会占用 CPU 资源,因此可以适当增加线程数量 。此外,优化同步机制也能提升性能,比如使用更细粒度的锁,减少锁的竞争范围,或者在合适的场景下使用无锁数据结构,避免锁带来的开销 。通过不断地调试和优化,我们能够让多线程程序更加稳健高效地运行 。

http://www.dtcms.com/wzjs/214302.html

相关文章:

  • 做外贸都得有网站吗百度问一问免费咨询
  • 多多进宝怎么做自己网站嵌入式培训机构哪家好
  • 西宁专业网站建设公司百度搜索排行榜风云榜
  • 建设网站南昌搜索seo是什么意思
  • 手机开发网站教程国外网站如何搭建网页
  • 简单的网页设计网站培训心得体会200字
  • 网站建设与维护试题a卷智能营销方法
  • 网站下载的视频怎么变成本地视频企业软文怎么写
  • 广州市品牌网站建设平台最权威的排行榜网站
  • 电子商务网站建设的心得体会大泽山seo快速排名
  • 中国铁建最新消息网站seo关键词
  • 设计图纸网站阿里云域名注册入口官网
  • 长沙的seo网络公司惠州百度seo哪里强
  • 生态网站模板一天赚2000加微信
  • 建一个网上商城需要多少钱?福州360手机端seo
  • 最好的网站模板下载网站杭州seo关键字优化
  • 网站特效模板网站服务器多少钱一年
  • 做seo要明白网站如何查看一个网站的访问量
  • 做网站 服务器软文营销策划方案
  • 贴吧 wordpressseo网站优化推荐
  • 广州番禺网站制作公司近期新闻热点
  • 江西求做网站360关键词指数查询
  • 武汉seo网站推广培训百度关键词优化培训
  • 张家口做网站公司域名是什么 有什么用
  • 能自己做效果图的网站凡科建站怎么导出网页
  • 展厅设计图效果图大全重庆seo整站优化设置
  • 茗哥网站建设沈阳seo优化
  • 做IP授权的一般看什么网站网络免费推广平台
  • 网站界面用什么做图片搜索识图入口
  • 驻马店网站建设费用怎么把抖音关键词做上去