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

【操作系统】操作系统实验02-生产者消费者程序改进

1. 说明文档中原有程序实现的功能、实现方法。(用语言、程序流程图、为原有程序添加注释等方式均可)

1.//const.h
2.//定义宏变量
3.#ifndef CONST_H
4.#define CONST_H
5.
6.#define TRUE 1
7.#define FALSE 0
8.#define ERROR 0
9.#define OVERFLOW -2
10.#define OK 1
11.#define MAXSIZE 20//队列中最多有20个元素
12.#define MAXLEN 100//字符串最大长度
13.
#endif
1.//Typedefine.h
2.//定义结构体
3.#ifndef TYPEDEFINE_H
4.#define TYPEDEFINE_H
5.
6.#include "const.h"
7.//字符串结构体
8.typedef struct{
9.	char data[MAXLEN];//数据
10.	int len;//数据长度,不算字符串结束字符”\0”
11.}SString;
12.//元素类型结构体
13.typedef struct{
14.	SString* strName;//数据
15.	long INumber;//号码
16.}Elemtype;
17.//结点结构体
18.typedef struct node{
19.	Elemtype data;//结点的数据域
20.	struct node *next;//结点的指针域,指向下一个结点地址
21.}SNode;
22.//队列结构体
23.typedef struct{
24.	Elemtype q[MAXSIZE];//存放队列元素
25.	int front;//队列头指针
26.	int rear;//队列尾指针,存放尾元素的下一个位置
27.}Queue;
28.//栈结构体
29.typedef struct st{
30.	Elemtype data[MAXSIZE];//存放栈元素
31.	int top;//栈顶指针
32.}Stack;
33.
34.typedef int BOOL;
35.typedef int Status;
36.
#endif
1.//Queue.h
2.//定义队列的相关函数
3.#ifndef QUEUE_H_LY
4.#define QUEUE_H_LY
5.
6.#include "Typedefine.h"
7.#include <stdbool.h>
8.
9.Queue *InitQueue();//初始化队列函数
10.int EnQueue(Queue *Q, Elemtype x);//入队函数
11.int DeQueue(Queue *Q, Elemtype *x);//出队函数
12.int QueueEmpty(Queue Q);//判断队列是否为空,非空返回0,空返回1
13.int QueueCount(Queue *HQ);//统计队列元素个数
14.bool QueueFull(Queue Q);//判断队列是否满,满返回 TRUE,非满返回 FALSE
15.
#endif
1.//Queue.c
2.//队列的相关函数实现代码
3.#include "stdio.h"
4.#include "malloc.h"
5.#include "const.h"
6.#include "Queue.h"
7.#include <stdbool.h>
8.//队列初始化函数
9.Queue *InitQueue(){
10.	Queue *Q = (Queue*)malloc(sizeof(Queue));//申请空间
11.	Q->front = Q->rear = 0;//头尾指针相等时说明队列为空
12.	return Q;//返回队列指针
13.}
14.//入队函数
15.int EnQueue(Queue *Q, Elemtype x){
16.	if((Q->rear + 1) % MAXSIZE == Q->front) return FALSE;//队满,入队失败,返回0
17.	else{
18.		Q->q[Q->rear] = x;//存放元素至队尾
19.		Q->rear = (Q->rear + 1) % MAXSIZE;//队列尾指针指向下一位
20.		return TRUE;
21.	}
22.}
23.//出队函数
24.int DeQueue(Queue *Q, Elemtype *x){
25.	if(Q->rear == Q->front) return FALSE;//队空,出队失败,返回0
26.	else{
27.		*x = Q->q[Q->front];//从队头取出元素
28.		Q->front = (Q->front + 1) % MAXSIZE;//队头指针指向下一位
29.		return TRUE;
30.	}
31.}
32.//判断队是否为空,非空返回0,空返回1
33.int QueueEmpty(Queue Q){
34.	if(Q.rear == Q.front) return TRUE;//队空
35.	else return FALSE;
36.}
37.//返回队列中的最后的一个元素
38.Elemtype Last(Queue *Q){
39.	Elemtype *prElem = NULL;
40.	Queue *prTempQueue;
41.	prTempQueue = InitQueue();//初始化一个新的临时队列,用于暂存数据
42.	while(QueueEmpty(*Q) == 1){//将Q中的元素依次取出放至prTempQueue中
43.		DeQueue(Q, prElem);
44.		EnQueue(prTempQueue, *prElem);
45.	}
46.	while(QueueEmpty(*prTempQueue) == 1){//将prTempQueue中的元素依次取出放至Q中
47.		DeQueue(prTempQueue, prElem);
48.		EnQueue(Q, *prElem);
49.	}
50.	return *prElem;//返回队列中的最后的一个元素
51.}
52.//判断队是否为满,满返回1,非满返回0
53.bool QueueFull(Queue Q){
54.	if(((Q.rear + 1) % MAXSIZE) == Q.front) return TRUE;//队满
55.	else return FALSE;
}
1.//exp1.c
2.//主程序,实现三个生产者与两个消费者问题
3.//在30s内,生产者把产品放入缓冲区,消费者从缓冲区中拿走产品,缓冲区空间定义为5,因为队列中元素最大值设为了20,所以无需考虑队列满情况。生产者在缓冲区满时必须等待,直到缓冲区有空间才继续生产;消费者在缓冲区空时必须等待,直到缓冲区中有产品才能继续读取。共有三个生产者与两个消费者线程,并将生产的商品放入队列中(商品编号从1000开始,最多放五个),根据先生产先消费的原则进行消费。
4.#include <stdio.h>
5.#include <stdlib.h>
6.#include <unistd.h>
7.#include <pthread.h>
8.#include <errno.h>
9.#include <sys/ipc.h>
10.#include <semaphore.h>
11.#include <fcntl.h>
12.#include "Queue.h"
13.#include "const.h"
14.#include "Queue.c"
15.
16.#define N 5//定义可用资源数量
17.
18.time_t end_time;
19.sem_t mutex, full, empty;
20.int fd;
21.Queue *qt;
22.Elemtype p;
23.void consumer(void *arg);
24.void productor(void *arg);
25.
26.int main(){
27.	pthread_t id1, id2, id3, id4, id5;//创建5个线程,分别对应三个生产者与两个消费者
28.	pthread_t mon_th_id;
29.	int ret;
30.	end_time = time(NULL) + 30;//程序执行30s停止
31.	qt = InitQueue();//初始化队列qt
32.	p.INumber = 1000;//号码从1000开始
33.	ret = sem_init(&mutex, 0, 1);//初使化互斥信号量mutex为1
34.	ret = sem_init(&empty, 0, N);//初使化empty信号量为N
35.
36.	ret = sem_init(&full, 0, 0);//初使化full信号量为0
37.	if(ret != 0) perror("sem_init");
38.
39.	ret = pthread_create(&id1, NULL, (void*)productor, NULL);//生产者线程1
40.	if(ret != 0) perror("pthread cread1");
41.
42.	ret = pthread_create(&id3, NULL, (void*)productor, NULL);//生产者线程3
43.	if(ret != 0) perror("pthread cread3");
44.
45.	ret = pthread_create(&id2, NULL, (void*)consumer, NULL);//消费者线程2
46.	if(ret != 0) perror("pthread cread2");
47.
48.	ret = pthread_create(&id5, NULL, (void*)productor, NULL);//生产者线程5
49.	if(ret != 0) perror("pthread cread5");
50.
51.	ret = pthread_create(&id4, NULL, (void*)consumer, NULL);//消费者线程4
52.	if(ret != 0) perror("pthread cread4");
53.
54.	pthread_join(id1, NULL);//等待生产者线程1结束
55.	pthread_join(id2, NULL);//等待消费者线程2结束
56.	pthread_join(id3, NULL);//等待生产者线程3结束
57.	pthread_join(id4, NULL);//等待消费者线程4结束
58.	pthread_join(id5, NULL);//等待生产者线程5结束
59.
60.	exit(0);
61.}
62.//生产者线程函数
63.void productor(void *arg){
64.	int i, nwrite;
65.	while(time(NULL) < end_time){//在规定时间内循环生产商品
66.		sem_wait(&empty);//empty信号量P操作
67.		sem_wait(&mutex);//互斥信号量P操作
68.		if(TRUE == QueueFull(*qt)){//队满不操作
69.			printf("Procuctor: buffer is full, please try to write later.\n");
70.		}
71.		else{//队不满
72.			EnQueue(qt, p);//入队
73.			printf("Productor: write [%d] to buffer\n",p.INumber);
74.			p.INumber++;//编号加一
75.		}
76.		sem_post(&full);//full信号量V操作
77.		sem_post(&mutex);//mutex信号量V操作
78.		sleep(1);
79.	}
80.}
81.
82.void consumer(void *arg){
83.	int nolock = 0;
84.	int ret, nread;
85.	Elemtype p2;
86.	while((time(NULL) < end_time) || (FALSE == (QueueEmpty(*qt)))){//在规定时间内或队列非空时,循环消费商品
87.		sem_wait(&full);//full信号量P操作
88.		sem_wait(&mutex);//互斥信号量P操作
89.		if(TRUE == QueueEmpty(*qt)){//队列空,不能消费
90.			printf("Consumer: the buffer is empty, please try to read later.\n");
91.		}
92.		else{//队列非空
93.			DeQueue(qt, &p2);//出队
94.			printf("Consumer: read [%d] from buffer.\n", p2.INumber);
95.		}
96.		sem_post(&empty);//empty信号量V操作
97.		sem_post(&mutex);//互斥信号量V操作
98.		sleep(2);
99.	}
}

2. 列出可改进的功能、实现方法等

可改进的功能:

  1. 将代码整理,去除掉了冗余代码。
  2. 显示缓冲区资源个数:创建全局变量n,统计生产者/消费者 生产/消费后缓冲区中剩余的资源个数,并予以显示。
  3. 提示生产者/消费者线程结束语句,如:Productor 1 is killed。
  4. 利用gettid()函数获得生产者、消费者的线程id,并显示是哪一个生产者/消费者 生产/消费 了哪一个商品。
  5. 编写了display()函数,根据生产者/消费者的动作,可视化了缓冲区中的商品状况,使商品变化情况更加简单直观。
  6. 利用for循环创建线程,实现可以自主输入想要创建的生产者与消费者个数(数值在1到10之间)、缓冲区大小(数值在1到20之间)与产品编号(数值在1到1000之间)。
  7. 编写了print()函数,优化了终端可视化界面,可在界面中选择功能并自定义赋值,使程序运行更灵活直观。

实现方法:

  1. 首先打印出终端可视化界面,让用户可以自主选择功能,可以进行自定义赋值、运行程序与退出系统操作,未进行自定义赋值的变量使用默认值。
  2. 自定义赋值后运行程序,根据用户的赋值给变量赋值,并创建生产者、消费者线程,运行生产者、消费者函数。
  3. 在生产者、消费者函数中,根据队列情况进行P、V操作与生产/消费操作,并利用gettid()函数获得生产者的线程id,显示是哪一个生产者/消费者 生产/消费了哪一件商品,并可视化生产/消费操作前后缓冲区中的产品变化情况与缓冲区中剩余的资源个数。
  4. 运行规定时间后生产者/消费者线程结束,退出系统。

3. 详细说明已完成的改进,附上程序代码,改进处加注释(注意代码格式)

1.//const.h
2.
3.#ifndef CONST_H
4.#define CONST_H
5.#define TRUE 1
6.#define FALSE 0
7.#define MAXSIZE 20
8.#define MAXLEN 100
#endif
1.//Typedefine.h
2.
3.#ifndef TYPEDEFINE_H
4.#define TYPEDEFINE_H
5.#include "const.h"
6.
7.typedef struct{
8.	char data[MAXLEN];
9.	int len;
10.}SString;
11.
12.typedef struct{
13.	SString* strName;
14.	long INumber;
15.}Elemtype;
16.
17.typedef struct{
18.	Elemtype q[MAXSIZE];
19.	int front;
20.	int rear;
21.}Queue;
22.
#endif
1.//Queue.h
2.
3.#ifndef QUEUE_H_LY
4.#define QUEUE_H_LY
5.
6.#include "Typedefine.h"
7.#include <stdbool.h>
8.
9.Queue *InitQueue();
10.int EnQueue(Queue *Q, Elemtype x);
11.int DeQueue(Queue *Q, Elemtype *x);
12.int QueueEmpty(Queue Q);
13.bool QueueFull(Queue Q);
14.
#endif
1.//Queue.c
2.
3.#include "stdio.h"
4.#include "malloc.h"
5.#include "const.h"
6.#include "Queue.h"
7.#include <stdbool.h>
8.
9.Queue *InitQueue(){
10.	Queue *Q = (Queue*)malloc(sizeof(Queue));
11.	Q->front = Q->rear = 0;
12.	return Q;
13.}
14.
15.int EnQueue(Queue *Q, Elemtype x){
16.	if((Q->rear + 1) % MAXSIZE == Q->front) return FALSE;
17.	else{
18.		Q->q[Q->rear] = x;
19.		Q->rear = (Q->rear + 1) % MAXSIZE;
20.		return TRUE;
21.	}
22.}
23.
24.int DeQueue(Queue *Q, Elemtype *x){
25.	if(Q->rear == Q->front) return FALSE;
26.	else{
27.		*x = Q->q[Q->front];
28.		Q->front = (Q->front + 1) % MAXSIZE;
29.		return TRUE;
30.	}
31.}
32.
33.int QueueEmpty(Queue Q){
34.	if(Q.rear == Q.front) return TRUE;
35.	else return FALSE;
36.}
37.
38.bool QueueFull(Queue Q){
39.	if(((Q.rear + 1) % MAXSIZE) == Q.front) return TRUE;
40.	else return FALSE;
}
1.//exp1.c
2.
3.#include <stdio.h>
4.#include <stdlib.h>
5.#include <unistd.h>
6.#include <pthread.h>
7.#include <errno.h>
8.#include <sys/ipc.h>
9.#include <semaphore.h>
10.#include <fcntl.h>
11.#include "Queue.h"
12.#include "const.h"
13.#include "Queue.c"
14.#include <sys/syscall.h>//使用gettid()函数所需要的头文件 
15.#define gettid() syscall(__NR_gettid)//使用gettid()函数所需要的头文件
16.#include <string.h>
17.
18.time_t end_time;
19.sem_t mutex, full, empty;
20.Queue *qt;
21.Elemtype p;
22.int n = 0;//缓冲区中资源个数
23.int x = 2, y = 2;//生产者与消费者个数(默认值) 
24.int ret;
25.int N = 5;//缓冲区最大资源个数(默认值) 
26.
27.void consumer(void *arg);
28.void productor(void *arg);
29.void display();//可视化缓冲区中商品状况
30.void print();//终端可视化界面 
31.
32.int main(){
33.	p.INumber = 1;//产品初始编号(默认值) 
34.	while(1){
35.		print();//展示系统可视化选项 
36.		//接收用户需要的功能号
37.		char number[100]; 
38.		ret = scanf("%s", &number[0]);
39.        if(strlen(number)!=1 || ret != 1 || number[0] < '0' || number[0] > '5'){
40.            printf("\nplease input a number between 0 and 5!\n");
41.            continue;
42.        }
43.        int num = number[0] - '0';
44.        //输入想要创建的生产者个数,并确保数值在1到10之间
45.        if(num == 1){	
46.			do{
47.				printf("Please input the number of productor: ");
48.				ret = scanf("%d",&x);
49.				if(ret != 1 || x < 1 || x > 10){
50.					printf("Please ensure the input between 1 and 10\n");
51.				}
52.			}while(ret != 1 || x < 1 || x > 10); 
53.    	}
54.    	//输入想要创建的消费者个数,并确保数值在1到10之间
55.    	else if(num == 2){
56.    		do{
57.				printf("Please input the number of consumer: ");
58.				ret = scanf("%d",&y);
59.				if(ret != 1 || y < 1 || y > 10){
60.					printf("Please ensure the input between 1 and 10\n");
61.				}
62.			}while(ret != 1 || y < 1 || y > 10);
63.		}
64.		//输入想要创建的缓冲区大小,并确保数值在1到20之间
65.		else if(num == 3){
66.			do{
67.				printf("Please input the size of buffer: ");
68.				ret = scanf("%d",&N);
69.				if(ret != 1 || N < 1 || N > 20){
70.					printf("Please ensure the input between 1 and 20\n");
71.				}
72.			}while(ret != 1 || N < 1 || N > 20); 
73.		}
74.		//输入想要生产的产品的初始编号,并确保数值在1到1000之间
75.		else if(num == 4){
76.			do{
77.				printf("Please input the key of product: ");
78.				ret = scanf("%d",&p.INumber);
79.				if(ret != 1 || p.INumber < 1 || p.INumber > 1000){
80.					printf("Please ensure the input between 1 and 1000\n");
81.				}
82.			}while(ret != 1 || p.INumber < 1 || p.INumber > 1000); 
83.		}
84.		//运行程序 
85.		else if(num == 5){	 
86.			pthread_t id;
87.			end_time = time(NULL) + 10;
88.			qt = InitQueue();
89.			//p.INumber = 1000;
90.			ret = sem_init(&mutex, 0, 1);
91.			ret = sem_init(&empty, 0, N);
92.	
93.			ret = sem_init(&full, 0, 0);
94.			if(ret != 0) perror("sem_init");
95.			//创建线程
96.			for(int i = 0; i < x; i++){
97.				ret = pthread_create(&id, NULL, (void*)productor, NULL);
98.				if(ret != 0) perror("Productor pthread create");
99.			}
100.	
101.			for(int i = x; i < x + y; i++){
102.				ret = pthread_create(&id, NULL, (void*)consumer, NULL);
103.				if(ret != 0) perror("Consumer pthread create");
104.			}
105.			//等待线程结束 
106.			for(int i = 0; i < x; i++){
107.				pthread_join(id, NULL);
108.				printf("Productor %d is killed\n", i);//提示程序结束语句
109.			}
110.			for(int i = x; i < x + y; i++){
111.				pthread_join(id, NULL);
112.				printf("Consumer %d is killed\n", i);//提示程序结束语句
113.			}
114.			break;
115.		}
116.		//退出系统 
117.   	 	else if(num == 0){
118.   	        break;
119.   	    } 	
120.	}
121.	return 0;
122.}
123.
124.void productor(void *arg){
125.	while(time(NULL) < end_time){
126.		sem_wait(&empty);
127.		sem_wait(&mutex);
128.		if(TRUE == QueueFull(*qt)){
129.			printf("Productor pthread %u: buffer is full, please try to write later.\n", gettid()); 
130.		}
131.		else{
132.			EnQueue(qt, p);
133.			display();//可视化缓冲区
134.			n++;//缓冲区资源数加一 
135.			printf("------->");
136.			display();//可视化缓冲区
137.			printf("\n");
138.			printf("Productor pthread %u: write [%d] to buffer.\n", gettid(), p.INumber);//利用gettid()函数获得生产者的线程id,并显示是哪一个生产者生产了哪一件商品
139.			printf("There are %d left in buffer\n",n);//显示缓冲区中剩余的资源个数
140.			printf("--------------------------------------------------\n");//生产者/消费者动作分隔符 
141.			p.INumber++;
142.		}
143.		sem_post(&full);
144.		sem_post(&mutex);
145.		sleep(1);
146.	}
147.}
148.
149.void consumer(void *arg){
150.	Elemtype p2;
151.	while((time(NULL) < end_time) || (FALSE == (QueueEmpty(*qt)))){
152.		sem_wait(&full);
153.		sem_wait(&mutex); 
154.		if(TRUE == QueueEmpty(*qt)){
155.			printf("Consumer pthread %u: the buffer is empty, please try to read later.\n", gettid());
156.		}	
157.		else{
158.			DeQueue(qt, &p2);
159.			display();//可视化缓冲区
160.			n--;//缓冲区资源数减一
161.			printf("------->");
162.			display();//可视化缓冲区
163.			printf("\n");
164.			printf("Consumer pthread %u: read [%d] from buffer.\n", gettid(), p2.INumber);//利用gettid()函数获得消费者的线程id,并显示是哪一个消费者消费了哪一件商品
165.			printf("There are %d left in buffer\n",n);//显示缓冲区中剩余的资源个数
166.			printf("--------------------------------------------------\n");//生产者/消费者动作分隔符 
167.		}
168.		sem_post(&empty);
169.		sem_post(&mutex);
170.		sleep(2);
171.	}
172.}
173.//可视化缓冲区中的商品状况
174.void display(){
175.	printf("[");
176.	for(int i = n; i > 0; i--){
177.		printf(" P ");
178.	}
179.	printf("]");	
180.}
181.//终端可视化界面
182.void print()
183.{    
184.    printf("\n");
185.    printf("*********************************************************************************\n");
186.    printf("--------------------[ Producer Consumer Program Function Bar ]-------------------\n");
187.    printf("*********************************************************************************\n");
188.    printf("*  1.Enter the number of producers you want to create (default value is 2)      *\n");
189.    printf("*  2.Enter the number of consumers you want to create (default value is 2)      *\n");
190.    printf("*  3.Enter the buffer size you want to create (default value is 5)              *\n");
191.    printf("*  4.Enter the initial product number you want to produce (default value is 1)  *\n");
192.    printf("*  5.run the program                                                            *\n");
193.    printf("*  0.Exit the system                                                            *\n");
194.    printf("---------------------------------------------------------------------------------\n");
195.    printf("Please enter 0-5 to select a function: ");
}

4. 运行结果截图

(1)示例一,这次运行我们想将生产者改为2个,消费者改为4个,其余选项为默认值,运行程序。

中间略

(2)示例二,这次运行我们想将缓冲区大小改为7,商品初始编号改为300,其余选项为默认值,运行程序。

中间略

5. 总结

实验中遇到的问题与解决方法:

问题1:想要改进程序执行语句,使程序运行时能反映出具体是哪一位生产者/消费者 生产/消费 了哪一个商品。一开始我尝试在pthread_create()函数中给生产者/消费者函数传递生产者/消费者id变量,但是由于线程共享变量,无法在生产者-生产者、消费者-消费者之间作出区分。

解决方法:最终我决定使用gettid()函数获得生产者、消费者的线程id,作为每一位生产者/消费者的标志,来反映出具体是哪一位生产者/消费者 生产/消费 了商品。

问题2:在程序运行过程中,缓冲区中的产品变化并不能被很好的反映出来。

解决方法:我编写了display()函数,分别在生产/消费前后调用它,可视化了缓冲区中的商品状况。

问题3:程序不够灵活,并且界面对用户不够友好。想要实现用户能够简单自主地控制想要创建的生产者与消费者个数、缓冲区大小与产品编号。

解决方法:我使用了全局变量,在程序执行之初定义好默认值,并提供了与用户交互的接口,使用户能够自主改变程序运行的参数。编写了print()函数,优化了终端可视化界面,使用户能够自主选择想要改变何种参数,使程序运行更灵活直观。

相关文章:

【操作系统】操作系统实验02-生产者消费者程序改进

1. 说明文档中原有程序实现的功能、实现方法。&#xff08;用语言、程序流程图、为原有程序添加注释等方式均可&#xff09; 1.//const.h 2.//定义宏变量 3.#ifndef CONST_H 4.#define CONST_H 5. 6.#define TRUE 1 7.#define FALSE 0 8.#define ERROR 0 9.#define OVERFLOW -…...

TCP协议是安全的吗?

不安全 虽然 TCP 提供了一种可靠且高效的数据传输方式&#xff0c;但它不提供任何加密或身份验证机制来保护数据。因此&#xff0c;传输的数据可能会被未经授权的用户拦截和读取&#xff0c;而且其真实性无法验证。 因此&#xff0c;为了确保 TCP 通信的安全&#xff0c;必须…...

c语言回顾-结构体(2)

前言 前面讲了结构体的概念&#xff0c;定义&#xff0c;赋值&#xff0c;访问等知识&#xff0c;本节内容小编将讲解结构体的内存大小的计算以及通过结构体实现位段&#xff0c;话不多说&#xff0c;直接上干货&#xff01;&#xff01;&#xff01; 1.结构体内存对齐 说到计…...

Prometheus常见exporter安装部署

Prometheus常见exporter安装部署 在稳定性环境的监控当中需要收集各种各样的数据&#xff0c;这样的数据收集是通过各种exporter进行的&#xff0c;在这里我们进行最常用稳定性数据的收集exporter安装部署介绍。 node_exporter安装部署 node_exporter主要监控服务器本身的一…...

DGit的使用

将Remix连接到远程Git仓库 1.指定克隆的分支和深度 2.清理&#xff0c;如果您不在工作区上工作&#xff0c;请将其删除或推送至 GitHub 或 IPFS 以确保安全。 为了进行推送和拉取&#xff0c;你需要一个 PAT — 个人访问令牌 当使用 dGIT 插件在 GitHub 上推送、拉取、访问私…...

ElasticSearch学习篇13_《检索技术核心20讲》进阶篇之LSM树

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243&#xff0c;文档形式记录笔记。 内容 磁盘和内存数据读取特点 工业界中数据量往往很庞大&#xff0c;比如数据无法全部加载进内存&#xff0c;无法支持索引的高效实时更新&…...

简单好用的C++日志库spdlog使用示例

文章目录 前言一、spdlog的日志风格fmt风格printf风格 二、日志格式pattern三、sink&#xff0c;多端写入四、异步写入五、注意事项六、自己封装了的代码usespdlog.h封装代码解释使用示例 前言 C日志库有很多&#xff0c;glog&#xff0c;log4cpp&#xff0c;easylogging, eas…...

python 方法运行计时装饰模式实现

在代码开发过程中&#xff0c;需要记录方法的执行时间&#xff0c;每个方法都硬代码也可以实现&#xff0c;但是不是最好的方式&#xff0c;考虑到设计模式和模版代码&#xff0c;通过装饰模式实现方法运行计时 在Python中&#xff0c;装饰器可以接受参数&#xff0c;这样可以…...

【权威出版/投稿优惠】2024年水利水电与能源环境科学国际会议(WRHEES 2024)

2024 International Conference on Water Resources, Hydropower, Energy and Environmental Science 2024年水利水电与能源环境科学国际会议 【会议信息】 会议简称&#xff1a;WRHEES 2024 大会时间&#xff1a;点击查看 截稿时间&#xff1a;点击查看 大会地点&#xff1a;…...

阿赵UE引擎C++编程学习笔记——场景加载和切换

大家好&#xff0c;我是阿赵。   继续学习UE引擎&#xff0c;这次来学习一下切换和加载场景的各种做法。 一、 蓝图实现 1、 切换关卡 所谓切换关卡&#xff0c;就是从当前关卡进入到一个新的关卡&#xff0c; 旧关卡的数据将会被放弃。进入新的关卡后&#xff0c;将会执行…...

【LLM之RAG】RAFT论文阅读笔记

研究背景 论文针对的主要问题是如何将预训练的大型语言模型&#xff08;LLMs&#xff09;适应特定领域的检索增强生成&#xff08;RAG&#xff09;。这些模型通常在广泛的文本数据上进行预训练&#xff0c;已经表现出在广义知识推理任务上的优越性能。然而&#xff0c;在特定领…...

【Android】使用Binder(AIDL)实现利用自定义Bean进行的进程间通信(二)

项目前置 这是我之前写的关于Binder的一些知识点和使用基本数据类型在通信的文章&#xff0c;感兴趣的可以看一下: Binder&#xff08;一&#xff09;Binder的介绍和AIDL使用Binder的实例 项目目标 在两个APP之间进行数据传递&#xff0c;使用Android推荐的Binder通讯&#…...

HTTP中get与post的区别?在传输数据类型上有什么区别?【面试】

HTTP中的GET和POST是两种最常见的请求方法&#xff0c;它们在数据传输和使用场景上有一些关键的区别&#xff1a; GET请求&#xff1a; 数据传输方式&#xff1a;GET请求将数据附加在URL之后&#xff0c;形成查询字符串&#xff08;namevalue的形式&#xff09;&#xff0c;数…...

「51媒体-年中大促」天津有哪些媒体资源-媒体宣传服务公司

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 天津的媒体资源相当丰富&#xff0c;涵盖了报纸、电视、广播、新闻门户网站、央媒驻天津机构、视频媒体以及全国媒体资源等多个方面。以下是详细的媒体资源分类和具体信息&#xff1a; 一…...

Thinkphp校园新闻发布系统源码 毕业设计项目实例

Thinkphp校园新闻发布系统源码 毕业设计项目实例 校园新闻发布系统模块&#xff1a; 用户模块&#xff1a;注册&#xff0c;登陆&#xff0c;查看个人信息&#xff0c;修改个人信息&#xff0c;站内搜索&#xff0c;新闻浏览等功能&#xff0c; 后台管理员模块&#xff1a;会员…...

前端老古董execCommand——操作 选中文本 样式

文章目录 ⭐前言⭐exe command api用法&#x1f496; example示例&#x1f496; 测试效果 ⭐execommand和getSelection 的联系⭐总结⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于 前端老古董execCommand——操作选中文本。 execommand 当一个 HTML 文…...

elementui写一个自定义的rangeInput的组件

组件定义 使用el-row确保元素都在一行上对外暴露的prop是minValue和maxValue&#xff0c;但是不建议直接使用&#xff0c;使用计算属性minValueComputed和maxValueComputed更改计算属性的值的不要直接更改计算属性&#xff0c;也不要直接更改原本的prop&#xff0c;通知外层的父…...

护眼灯哪些牌子好?一文刨析护眼灯怎么选择!

护眼灯哪些牌子好&#xff1f;护眼台灯作为对抗视力挑战的一种方法&#xff0c;逐渐赢得了众多家长的青睐。这些台灯利用尖端光学技术&#xff0c;发出柔和且无刺激的照明&#xff0c;有助于保护眼睛不受伤害。它们不但可以调节亮度和色温&#xff0c;打造一个舒适且自然的阅读…...

抖音短剧看剧系统是怎么做的?怎么样搭建上线运营?

前言&#xff1a; 当前热门短剧已深入大家的日常&#xff0c;针对一些好的短剧更是吸金无数。今天给大家介绍一下短剧这个项目整个运作模式。 一、一部短剧是怎么样呈现到观众眼前的&#xff1f; 首先影视作品公司拍摄剪辑好短剧 &#xff0c;弄好一切审核后&#xff0c;放到…...

2024.06.06校招 实习 内推 面经

绿*泡*泡VX&#xff1a; neituijunsir 交流*裙 &#xff0c;内推/实习/校招汇总表格 1、校招 | 追觅科技2025届校园招聘/正式启动&#xff01; 校招 | 追觅科技2025届校园招聘正式启动&#xff01; 2、校招&实习&社招 | 博世海外招聘—德国/专场正式启动&#xff0…...

神经网络模型---ResNet

一、ResNet 1.导入包 import tensorflow as tf from tensorflow.keras import layers, models, datasets, optimizersoptimizers是用于更新模型参数以最小化损失函数的算法 2.加载数据集、归一化、转为独热编码的内容一致 3.增加颜色通道 train_images train_images[...,…...

Linux之网络编程

Linux之网络编程 TCP协议 TCP(Transmission ControlProtocol) : 传输控制协议&#xff0c;是一个 面向连接的、可靠的、基于字节流的传输层的协议。TCP 协议建立的是一种点到点的&#xff0c;一对一的可靠连接协议 特点&#xff1a; 数据无丢失数据无失序数据无错误数据无重…...

opencascade AIS_InteractiveContext源码学习1

AIS_InteractiveContext 前言 交互上下文&#xff08;Interactive Context&#xff09;允许您在一个或多个视图器中管理交互对象的图形行为和选择。类方法使这一操作非常透明。需要记住的是&#xff0c;对于已经被交互上下文识别的交互对象&#xff0c;必须使用上下文方法进行…...

TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法

TIA博途 WinCC下载到面板时,提示错误消息:“装载过程终止由于传输错误:8020AB001A06FFF4!”的解决办法 这个错误信息是由于缺少设备镜像无法下载到操作面板而导致的。 当使用 TIA V15.1 Update 4 和 Update 5 组态 TP1000F Mobile 时,请遵守特别注意事项。 问题 在编译一个…...

【MySQL】聊聊数据库是如何保证数据不丢的

对于一个存储系统来说&#xff0c;其中比较关键的核心组件包含&#xff0c;网络、存储模型、持久化、数据结构等。而数据如何保证不丢失&#xff0c;对于不同的存储系统来说&#xff0c;比如Redis采用AOF和RDB的方式进行混合使用&#xff0c;而MySQL采用日志进行保证。也就是re…...

GitLab教程(四):分支(branch)和合并(merge)

文章目录 1.分支&#xff08;branch&#xff09;&#xff08;1&#xff09;分支的概念&#xff08;2&#xff09;branch命令 2.合并&#xff08;merge&#xff09;&#xff08;1&#xff09;三个命令pullfetchmergegit fetchgit mergegit pull &#xff08;2&#xff09;合并冲…...

2021数学建模A题目–“FAST”主动反射面的形状调节

A 题——“FAST”主动反射面的形状调节 思路&#xff1a;该题主要是通过利用伸缩杆调整FAST反射面&#xff0c;给出合适的调整方案 程序获取 第一题问题思路与结果&#xff1a; 当待观测天体S位于基准球面正上方&#xff0c;结合考虑反射面板调节因素&#xff0c;确定理想抛物…...

华为---- RIP路由协议基本配置

08、RIP 8.1 RIP路由协议基本配置 8.1.1 原理概述 RIP(Routing Information Protocol,路由协议)作为最早的距离矢量IP路由协议&#xff0c;也是最先得到广泛使用的一种路由协议&#xff0c;采用了Bellman-Ford算法&#xff0c;其最大的特点就是配置简单。 RIP协议要求网络中…...

Android studio在Ubuntu桌面上 创建桌面图标,以及导航栏图标

Android studio在Ubuntu桌面上 创建桌面图标&#xff0c;以及导航栏图标 1. 下载Android studio for Lunux 免安装版本之后&#xff0c;解压 2. 通过控制台运行 ~/Documents/android-studio-2024.1.1.2-linux/android-studio/bin$ ./studio.sh 3. 选择菜单&#xff0c;Tools…...

JAVA云HIS医院管理系统源码 云HIS系统的应用场景

JAVA云HIS医院管理系统源码 云HIS系统的应用场景 云HIS是针对中小医疗健康机构推出的一套基于云端的诊所云HIS服务平台&#xff0c;包括内部管理系统、临床辅助决策系统、体检系统、客户管理与服务系统、健康管理系统、知识管理系统、医患沟通系统、线上营销系统、其他外部系…...

工装网站建设方案/dw友情链接怎么设置

nginx常用命令&#xff1a;(根目录下执行 ../nginx-1.20.1/) 启动Start nginx (或 nginx.exe&#xff09; 从容停止服务 这种方法较stop相比就比较温和一些了&#xff0c;需要进程完成当前工作后再停止。 nginx -s quit 立即停止服务 这种方法比较强硬&#xff0c;无论进程…...

外贸网站产品分析/打开百度官网

AS3中新的XML处理方法&#xff1a;E4X&#xff0c;直到现在&#xff0c;ECMA脚本语言规范&#xff08;ECMA-262&#xff09;&#xff0d;&#xff0d;AscriptScript 3.0的核心基础&#xff0c;并没有提供任何的XML数据处理类或方法。AcriontScript之前的版本&#xff08;从Flas…...

网站开发项目比赛/百度推广网站平台

1. 问题描述&#xff1a; 有 N 种物品和一个容量是 V 的背包。第 i 种物品最多有 si 件&#xff0c;每件体积是 vi&#xff0c;价值是 wi。求解将哪些物品装入背包&#xff0c;可使物品体积总和不超过背包容量&#xff0c;且价值总和最大。输出最大价值。 输入格式 第一行两…...

日照 网站建设/中国seo高手排行榜

关键字&#xff1a;Qt4 Qt5 区别 模块介绍 Qt 5 与 Qt 4 最大的一个区别之一是底层架构有了修改。Qt 5 引入了模块化的概念&#xff0c;将众多功能细分到几个模块之中。Qt 4 也有模块的概念&#xff0c;但是是一种很粗的划分&#xff0c;而 Qt 5 则更加细化。本文主要对 Qt…...

视频网站开发技术书/qq代刷网站推广

在我们使用AnnotationConfigApplicationContext类的时候&#xff0c;它有一个父类GenericApplicationContext。 我们都知道&#xff0c;使用子类的时候会自动执行父类的构造方法&#xff0c;而这个父类的构造方法中有一个对象DefaultListableBeanFactory 如图&#xff1a; 这个…...

wordpress修改发布时间/最新营销模式

免费手机WAP网站大全免费手机WAP网站大全1 手机之家 推荐指数&#xff1a;★★★★手机地址&#xff1a;http://wap.imbile.com.cn自动识别您手机的机型、自动帮你分类适合您手机的图铃、手机主题等可以在线查找号码归属地 天气预报 等众多娱乐项目完全免费&#xff12;、移动…...