【QT入门到晋级】进程间通信(IPC)-共享内存

前言

       前面分享了几种IPC通信技术,都有成熟的交互机制(阻塞和非阻塞方式交互),而本文分享的共享内存,更像是系统提供了一张“白纸”,让多个进程自己构建管理及安全机制,而有些场景只需要简单的机制,此时反复利用的共享内存是非常高效的。

概述

       共享内存是一种高效的进程间通信(IPC, Interprocess Communication)‌ 机制,允许多个进程访问同一块物理内存区域,从而实现数据共享。它的核心原理涉及 内存映射、内核管理、进程地址空间映射‌等关键技术。   

特性讲解

        以下几种IPC的对比分析

特性

共享内存

管道

Socket (本地)

​数据拷贝次数​

​​0次​(直接访问)

2次(用户态<->内核态)

2次(用户态<->内核态)

​系统调用​

​​无需​​(访问时)

每次读写都需要

每次读写都需要

​内核参与​

​​仅初始映射和同步​​

​​全程中介​​

​​全程中介+协议栈​​

​延迟​

​​极低​​(纳秒级,接近RAM速度)

较低(微秒级)

高(微秒级甚至毫秒级)

​吞吐量​

​​高​​(由内存带宽决定)

中等

中等(受协议开销影响)

​复杂度​

​​高​​(需自行处理同步和竞态条件)

优点-访问快

        共享内存是用户态提供虚拟地址空间映射到物理内存中,此时访问内存不需要IO切换,属于直接访问,即实现了数据零拷贝,而管道和socket都需要调用wirte/read,需要对数据进行用户态<->内核态之间的IO切换,所以IPC中,共享内存处理数据具备低延时、高吞吐的特性。

缺点-复杂度高

        共享内存本身不提供任何进程间的同步机制。如果多个进程同时读写同一块区域,会导致数据竞争(Data Race)和混乱。开发者必须使用其他IPC机制(如​​信号量(Semaphore)、互斥锁(Mutex)、条件变量(Condition Variable)​​)来保护共享内存区域,实现有序的访问。这些同步操作本身也会带来一些开销,但通常远低于数据拷贝的开销。

        与之相比,管道和Socket套接字,编程上代码非常简洁,而且内置了同步(阻塞/非阻塞IO)机制,这些机制能满足大部分的通信交互场景。所以如果对延迟不是非常敏感,而更希望是开发起来简单、安全、可移植的话,同一台设备中的进程通信就使用管道,两台不同设备通信就用socket套接字。

        总结:共享内存用更复杂的编程模型换取了极致的性能,而管道/Socket用一部分性能换取了更简单、更安全的编程模型。

场景应用

进程内读大文件

       视频文件通常都会很大,如果用传统的read(),需要加载N个buff[buff_size]才能把视频文件加载到内存中,而mmap只需要一次映射即可。

#include <iostream>
#include <thread>
#include <atomic>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>// 原子变量控制读线程终止
std::atomic<bool> stop_reading(false);/*** 读线程函数:从内存映射区域读取视频帧* @param mapped_data 内存映射起始地址* @param file_size 视频文件总大小* @param frame_size 每帧数据大小(字节)*/
void read_frames(const char* mapped_data, size_t file_size, int frame_size) {// 按帧大小遍历整个文件for (size_t offset = 0; offset < file_size && !stop_reading; offset += frame_size) {// 计算当前帧起始位置const char* frame = mapped_data + offset;// 实际应用中这里应替换为真实的帧处理逻辑// 例如:解码H.264帧或渲染图像std::cout << "Read frame at offset: " << offset << std::endl;// 模拟30fps的视频播放速率(33ms/帧)std::this_thread::sleep_for(std::chrono::milliseconds(33));}
}int main(int argc, char** argv) {// 参数检查if (argc < 3) {std::cerr << "Usage: " << argv[0] << " <video_file> <frame_size>" << std::endl;return 1;}const char* filename = argv[1];      // 视频文件路径const int frame_size = std::stoi(argv[2]);  // 每帧数据大小(字节)// 1. 打开视频文件(只读模式)int fd = open(filename, O_RDONLY);if (fd == -1) {perror("open failed");return 1;}// 2. 获取文件状态信息(主要需要文件大小)struct stat sb;if (fstat(fd, &sb) == -1) {perror("fstat failed");close(fd);return 1;}// 3. 创建内存映射(私有映射,避免修改原文件)char* mapped_data = static_cast<char*>(// mmap参数说明://   - NULL:由内核自动选择映射地址//   - sb.st_size:映射区域长度(文件大小)//   - PROT_READ:映射区域可读//   - MAP_PRIVATE:私有映射(修改不写回文件)//   - fd:文件描述符//   - 0:偏移量(从文件开头映射)// 返回值://   - 成功:映射区域起始地址(转换为char*类型)//   - 失败:MAP_FAILED(值为(void*)-1)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0));//通过fd句柄加载视频文件内容到共享内存中if (mapped_data == MAP_FAILED) {perror("mmap failed");close(fd);return 1;}// 4. 启动读线程(传递映射地址、文件大小和帧大小)std::thread reader(read_frames, mapped_data, sb.st_size, frame_size);// 5. 主线程等待用户输入终止std::cout << "Press Enter to stop reading..." << std::endl;std::cin.get();stop_reading = true;  // 通知读线程停止// 6. 清理资源reader.join();         // 等待读线程结束munmap(mapped_data, sb.st_size);  // 解除内存映射close(fd);             // 关闭文件描述符return 0;
}

mmap与read/write性能对比可以参见博文【QT入门到晋级】内存-CSDN博客

多进程间共享内存

        redis是一个高效的缓存数据库,实现热点数据快速响应,也是需要结合shm+mmap来实现的。项目中如果需要某些热点数据能够低延时的响应时(特别是大对象数据),就需要到shm+mmap的场景。

说明:以下代码实现,用到AI辅助实现。

共享内存数据结构定义

// shared_cache.h
#ifndef SHARED_CACHE_H
#define SHARED_CACHE_H#include <stdint.h>
#include <sys/types.h>
#include <pthread.h>#define SHM_CACHE_NAME "/shared_cache_db"
#define MAX_KEY_LENGTH 256
#define MAX_VALUE_SIZE 4096
#define HASH_TABLE_SIZE 1024// 缓存条目结构
typedef struct CacheEntry {char key[MAX_KEY_LENGTH];char value[MAX_VALUE_SIZE];size_t value_size;time_t expiry_time;      // 过期时间(0表示永不过期)time_t last_accessed;    // 最后访问时间uint32_t access_count;   // 访问计数struct CacheEntry* next; // 哈希冲突时使用链表
} CacheEntry;// 共享内存缓存结构
typedef struct SharedCache {CacheEntry* hash_table[HASH_TABLE_SIZE]; // 哈希表pthread_rwlock_t rwlock;                 // 读写锁(支持多个读者或一个写者)uint64_t total_entries;                  // 总条目数uint64_t total_hits;                     // 总命中次数uint64_t total_misses;                   // 总未命中次数size_t memory_used;                      // 已使用内存大小size_t memory_limit;                     // 内存限制int initialized;                         // 初始化标志
} SharedCache;// 函数声明
int create_shared_cache();
SharedCache* attach_shared_cache();
void detach_shared_cache(SharedCache* cache);
void destroy_shared_cache();// 缓存操作API
int cache_set(SharedCache* cache, const char* key, const char* value, size_t value_size, time_t expiry);
char* cache_get(SharedCache* cache, const char* key, size_t* value_size);
int cache_delete(SharedCache* cache, const char* key);
int cache_exists(SharedCache* cache, const char* key);
uint64_t cache_stats_hits(SharedCache* cache);
uint64_t cache_stats_misses(SharedCache* cache);
void cache_clear_expired(SharedCache* cache);#endif // SHARED_CACHE_H

共享内存管理

// shared_cache.c
#include "shared_cache.h"
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>// 简单的哈希函数
static uint32_t hash_key(const char* key) {uint32_t hash = 5381;int c;while ((c = *key++)) {hash = ((hash << 5) + hash) + c; // hash * 33 + c}return hash % HASH_TABLE_SIZE;
}// 创建共享内存缓存
int create_shared_cache() {int shm_fd = shm_open(SHM_CACHE_NAME, O_CREAT | O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open");return -1;}if (ftruncate(shm_fd, sizeof(SharedCache)) == -1) {perror("ftruncate");close(shm_fd);return -1;}close(shm_fd);return 0;
}// 附加到共享内存缓存
SharedCache* attach_shared_cache() {int shm_fd = shm_open(SHM_CACHE_NAME, O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open");return NULL;}SharedCache* cache = mmap(NULL, sizeof(SharedCache), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);close(shm_fd);if (cache == MAP_FAILED) {perror("mmap");return NULL;}return cache;
}// 分离共享内存缓存
void detach_shared_cache(SharedCache* cache) {if (cache != NULL) {munmap(cache, sizeof(SharedCache));}
}// 销毁共享内存缓存
void destroy_shared_cache() {shm_unlink(SHM_CACHE_NAME);
}// 初始化共享内存缓存(第一次使用时调用)
void init_shared_cache(SharedCache* cache) {if (cache->initialized) {return;}// 初始化哈希表memset(cache->hash_table, 0, sizeof(cache->hash_table));// 初始化读写锁属性pthread_rwlockattr_t rwlock_attr;pthread_rwlockattr_init(&rwlock_attr);pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED);// 初始化读写锁pthread_rwlock_init(&cache->rwlock, &rwlock_attr);// 初始化统计信息cache->total_entries = 0;cache->total_hits = 0;cache->total_misses = 0;cache->memory_used = 0;cache->memory_limit = 1024 * 1024 * 100; // 100MB默认限制cache->initialized = 1;
}// 设置缓存值
int cache_set(SharedCache* cache, const char* key, const char* value, size_t value_size, time_t expiry) {if (!cache->initialized) {init_shared_cache(cache);}if (value_size > MAX_VALUE_SIZE) {fprintf(stderr, "Value too large: %zu bytes (max: %d)\n", value_size, MAX_VALUE_SIZE);return -1;}// 计算哈希值uint32_t hash = hash_key(key);// 获取写锁pthread_rwlock_wrlock(&cache->rwlock);// 检查是否已存在相同键CacheEntry* entry = cache->hash_table[hash];CacheEntry* prev = NULL;while (entry != NULL) {if (strcmp(entry->key, key) == 0) {// 更新现有条目cache->memory_used -= entry->value_size;memcpy(entry->value, value, value_size);entry->value_size = value_size;entry->expiry_time = expiry;entry->last_accessed = time(NULL);cache->memory_used += value_size;pthread_rwlock_unlock(&cache->rwlock);return 0;}prev = entry;entry = entry->next;}// 创建新条目CacheEntry* new_entry = malloc(sizeof(CacheEntry));if (new_entry == NULL) {pthread_rwlock_unlock(&cache->rwlock);fprintf(stderr, "Failed to allocate memory for cache entry\n");return -1;}strncpy(new_entry->key, key, MAX_KEY_LENGTH - 1);new_entry->key[MAX_KEY_LENGTH - 1] = '\0';memcpy(new_entry->value, value, value_size);new_entry->value_size = value_size;new_entry->expiry_time = expiry;new_entry->last_accessed = time(NULL);new_entry->access_count = 0;new_entry->next = NULL;// 添加到哈希表if (prev == NULL) {cache->hash_table[hash] = new_entry;} else {prev->next = new_entry;}cache->total_entries++;cache->memory_used += sizeof(CacheEntry) + value_size;pthread_rwlock_unlock(&cache->rwlock);return 0;
}// 获取缓存值
char* cache_get(SharedCache* cache, const char* key, size_t* value_size) {if (!cache->initialized) {*value_size = 0;return NULL;}// 计算哈希值uint32_t hash = hash_key(key);// 获取读锁pthread_rwlock_rdlock(&cache->rwlock);CacheEntry* entry = cache->hash_table[hash];while (entry != NULL) {if (strcmp(entry->key, key) == 0) {// 检查是否过期if (entry->expiry_time > 0 && time(NULL) > entry->expiry_time) {cache->total_misses++;pthread_rwlock_unlock(&cache->rwlock);*value_size = 0;return NULL;}// 更新访问信息entry->last_accessed = time(NULL);entry->access_count++;cache->total_hits++;// 返回值*value_size = entry->value_size;// 需要复制值,因为调用者需要释放内存char* value_copy = malloc(entry->value_size);if (value_copy != NULL) {memcpy(value_copy, entry->value, entry->value_size);}pthread_rwlock_unlock(&cache->rwlock);return value_copy;}entry = entry->next;}cache->total_misses++;pthread_rwlock_unlock(&cache->rwlock);*value_size = 0;return NULL;
}// 检查键是否存在
int cache_exists(SharedCache* cache, const char* key) {if (!cache->initialized) {return 0;}uint32_t hash = hash_key(key);pthread_rwlock_rdlock(&cache->rwlock);CacheEntry* entry = cache->hash_table[hash];while (entry != NULL) {if (strcmp(entry->key, key) == 0) {int exists = (entry->expiry_time == 0 || time(NULL) <= entry->expiry_time);pthread_rwlock_unlock(&cache->rwlock);return exists;}entry = entry->next;}pthread_rwlock_unlock(&cache->rwlock);return 0;
}// 删除缓存项
int cache_delete(SharedCache* cache, const char* key) {if (!cache->initialized) {return -1;}uint32_t hash = hash_key(key);pthread_rwlock_wrlock(&cache->rwlock);CacheEntry* entry = cache->hash_table[hash];CacheEntry* prev = NULL;while (entry != NULL) {if (strcmp(entry->key, key) == 0) {if (prev == NULL) {cache->hash_table[hash] = entry->next;} else {prev->next = entry->next;}cache->memory_used -= sizeof(CacheEntry) + entry->value_size;cache->total_entries--;free(entry);pthread_rwlock_unlock(&cache->rwlock);return 0;}prev = entry;entry = entry->next;}pthread_rwlock_unlock(&cache->rwlock);return -1;
}// 获取命中次数统计
uint64_t cache_stats_hits(SharedCache* cache) {if (!cache->initialized) {return 0;}pthread_rwlock_rdlock(&cache->rwlock);uint64_t hits = cache->total_hits;pthread_rwlock_unlock(&cache->rwlock);return hits;
}// 获取未命中次数统计
uint64_t cache_stats_misses(SharedCache* cache) {if (!cache->initialized) {return 0;}pthread_rwlock_rdlock(&cache->rwlock);uint64_t misses = cache->total_misses;pthread_rwlock_unlock(&cache->rwlock);return misses;
}// 清理过期缓存项
void cache_clear_expired(SharedCache* cache) {if (!cache->initialized) {return;}time_t now = time(NULL);pthread_rwlock_wrlock(&cache->rwlock);for (int i = 0; i < HASH_TABLE_SIZE; i++) {CacheEntry* entry = cache->hash_table[i];CacheEntry* prev = NULL;while (entry != NULL) {if (entry->expiry_time > 0 && now > entry->expiry_time) {CacheEntry* to_delete = entry;if (prev == NULL) {cache->hash_table[i] = entry->next;} else {prev->next = entry->next;}entry = entry->next;cache->memory_used -= sizeof(CacheEntry) + to_delete->value_size;cache->total_entries--;free(to_delete);} else {prev = entry;entry = entry->next;}}}pthread_rwlock_unlock(&cache->rwlock);
}

生产者进程

// producer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "shared_cache.h"// 模拟从数据库或外部源加载热点数据
void load_hot_data(SharedCache* cache) {printf("Producer: Loading hot data into cache...\n");// 模拟一些热点数据cache_set(cache, "user:1001:profile", "{\"name\":\"Alice\",\"email\":\"alice@example.com\"}", 45, 0);cache_set(cache, "user:1002:profile", "{\"name\":\"Bob\",\"email\":\"bob@example.com\"}", 43, 0);cache_set(cache, "product:2001:info", "{\"name\":\"Laptop\",\"price\":999.99}", 35, 0);cache_set(cache, "product:2002:info", "{\"name\":\"Phone\",\"price\":499.99}", 34, 0);// 设置一些有过期时间的数据time_t expiry = time(NULL) + 300; // 5分钟后过期cache_set(cache, "session:abc123", "user_id=1001&role=admin", 22, expiry);printf("Producer: Hot data loaded successfully\n");
}// 定期更新缓存中的数据
void update_cache_data(SharedCache* cache) {static int counter = 0;counter++;char key[50];char value[100];// 模拟更新一些数据snprintf(key, sizeof(key), "stats:request_count");snprintf(value, sizeof(value), "%d", counter);cache_set(cache, key, value, strlen(value) + 1, 0);printf("Producer: Updated %s = %s\n", key, value);
}int main() {printf("Starting producer process...\n");// 创建共享内存缓存if (create_shared_cache() != 0) {fprintf(stderr, "Failed to create shared cache\n");return 1;}// 附加到共享内存缓存SharedCache* cache = attach_shared_cache();if (cache == NULL) {fprintf(stderr, "Failed to attach to shared cache\n");return 1;}// 初始化缓存init_shared_cache(cache);// 加载初始热点数据load_hot_data(cache);// 主循环:定期更新缓存while (1) {sleep(10); // 每10秒更新一次// 清理过期数据cache_clear_expired(cache);// 更新缓存数据update_cache_data(cache);// 输出统计信息uint64_t hits = cache_stats_hits(cache);uint64_t misses = cache_stats_misses(cache);uint64_t total = hits + misses;double hit_rate = total > 0 ? (double)hits / total * 100 : 0;printf("Producer: Cache stats - Hits: %lu, Misses: %lu, Hit Rate: %.2f%%\n", hits, misses, hit_rate);}// 清理资源detach_shared_cache(cache);return 0;
}

消费者进程

// consumer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "shared_cache.h"void process_user_request(SharedCache* cache, int user_id) {char key[50];snprintf(key, sizeof(key), "user:%d:profile", user_id);size_t value_size;char* value = cache_get(cache, key, &value_size);if (value != NULL) {printf("Consumer %d: Found user profile: %s\n", getpid(), value);free(value);} else {printf("Consumer %d: User profile not found in cache, would query DB\n", getpid());// 这里可以添加从数据库加载数据的逻辑,然后更新缓存}
}void process_product_request(SharedCache* cache, int product_id) {char key[50];snprintf(key, sizeof(key), "product:%d:info", product_id);size_t value_size;char* value = cache_get(cache, key, &value_size);if (value != NULL) {printf("Consumer %d: Found product info: %s\n", getpid(), value);free(value);} else {printf("Consumer %d: Product info not found in cache, would query DB\n", getpid());}
}void process_session_request(SharedCache* cache, const char* session_id) {char key[50];snprintf(key, sizeof(key), "session:%s", session_id);size_t value_size;char* value = cache_get(cache, key, &value_size);if (value != NULL) {printf("Consumer %d: Found session: %s\n", getpid(), value);free(value);} else {printf("Consumer %d: Session not found or expired\n", getpid());}
}int main() {printf("Starting consumer process %d...\n", getpid());// 附加到共享内存缓存SharedCache* cache = attach_shared_cache();if (cache == NULL) {fprintf(stderr, "Failed to attach to shared cache\n");return 1;}// 模拟处理请求int request_count = 0;while (request_count < 20) {// 模拟不同类型的请求int request_type = rand() % 3;switch (request_type) {case 0:process_user_request(cache, 1001 + (rand() % 3));break;case 1:process_product_request(cache, 2001 + (rand() % 3));break;case 2:process_session_request(cache, "abc123");break;}request_count++;sleep(1 + (rand() % 2)); // 随机等待1-2秒}// 输出消费者统计uint64_t hits = cache_stats_hits(cache);uint64_t misses = cache_stats_misses(cache);uint64_t total = hits + misses;double hit_rate = total > 0 ? (double)hits / total * 100 : 0;printf("Consumer %d: Final stats - Hits: %lu, Misses: %lu, Hit Rate: %.2f%%\n", getpid(), hits, misses, hit_rate);// 清理资源detach_shared_cache(cache);return 0;
}

编译及运行

# 编译共享缓存库
gcc -c -fPIC shared_cache.c -o shared_cache.o -lpthread
gcc -shared -o libsharedcache.so shared_cache.o -lpthread# 编译生产者
gcc -o producer producer.c -L. -lsharedcache -lpthread# 编译消费者
gcc -o consumer consumer.c -L. -lsharedcache -lpthread

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94538.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动化测试概念与 Web 自动化实战(基于 Selenium)

在软件测试领域&#xff0c;自动化测试是提升测试效率、保障回归测试质量的核心手段。尤其对于 C 开发的项目&#xff0c;自动化测试能有效减少重复手工操作&#xff0c;避免新增功能对历史功能的影响。本文从自动化基础概念入手&#xff0c;详解自动化分类、Web 自动化测试核心…

NeRAF、ImVid论文解读

目录 一、NeRAF 1、概述 2、方法 3、训练过程 4、实验 二、ImVid 1、概述 2、Imvid数据集 3、STG方法 一、NeRAF 1、概述 NeRF类方法仅支持视觉合成功能&#xff0c;缺乏声学建模能力。对于以往的声学建模&#xff08;如NAR/INRAS&#xff09;会忽略三维场景几何对声…

重复文件删除查找工具 Duplicate Files Search Link v10.7.0

软件介绍 Duplicate Same Files Searcher 是一款面向 Windows 平台的专业重复文件检索与清理工具&#xff0c;兼具符号链接替换与 NTFS 高级特性支持&#xff0c;可在无损数据的前提下大幅缩减磁盘冗余。 软件使用 软件打开后是英文版&#xff0c;手动切换中文&#xff08;按…

简易shell

目录 一、整体功能概述 函数准备 1.env命令 2.getenv()函数 3.snprintf 4.strtok()函数 三、全局变量 四、核心功能函数解析 1. 信息获取函数 2. 命令行交互 3. 命令解析 4. 普通命令执行 5. 内置命令处理&#xff08;核心功能&#xff09; 五、主函数流程 六、总…

网关资源权限预加载:从冷启动阻塞到优雅上线的完整闭环

网关资源权限预加载:从冷启动阻塞到优雅上线的完整闭环 基于 Spring Cloud Gateway + Spring Cloud Alibaba Nacos ——一篇可落地的技术方案与源码级实现 1. 场景与痛点 在微服务网关层做 统一资源权限校验 时,必须满足: 启动阻塞:所有权限规则加载完成前,不监听端口,拒…

open webui源码分析8—管道

我们可以把Open WebUI想象成一个管道系统&#xff0c;数据通过管道和阀门流动。管道作为open webui的插件&#xff0c;可以为数据构建新的通路&#xff0c;可以自定义逻辑和处理数据&#xff1b;阀门是管道的可配置部件&#xff0c;控制数据流过管道时的行为。管道可以理解成用…

深入理解 C 语言 hsearch 哈希表:限制、技巧与替代方案

概述 C 语言标准库中的 hsearch 系列函数提供了一套简单易用的哈希表实现,包含在 <search.h> 头文件中。这组函数虽然接口简洁,但在实际使用中存在一些重要的限制和注意事项。本文将深入探讨 hsearch 的功能特点、设计局限,并提供实用的解决方案和替代建议。 hsearc…

Web网络开发 -- HTML和CSS基础

HTML 超文本编辑语言 HTML 介绍 HTML的英文全称是 Hyper Text Markup Language&#xff0c;即超文本标记语言。HTML是由WEB的发明者 Tim Berners-Lee &#xff08;蒂姆伯纳斯李&#xff09;和同事 Daniel W. Connolly于1990年创立的一种标记语言&#xff0c; 它是标准通用化标…

Python爬虫实战:研究开源的高性能代理池,构建电商数据采集和分析系统

1. 绪论 1.1 研究背景与意义 随着互联网技术的飞速发展,网络数据已成为信息时代的核心资源之一。从商业角度看,企业通过分析竞争对手的产品信息、用户评价等数据,可制定更精准的市场营销策略;从学术研究角度,研究者通过爬取社交媒体数据、学术文献等,可开展社会网络分析…

项目设计文档——爬虫项目(爬取天气预报)

一、项目背景以及项目意义 项目背景&#xff1a; 爬虫技术的核心目的是自动化地从互联网上采集&#xff0c;提取和存储数据。网络爬虫是一种自动化程序&#xff0c;用于从互联网上抓取数据并进行处理。C语言因其高效性和接近硬件的特性&#xff0c;常被用于开发高性能的网络爬…

Python 操作 PPT 文件:从新手到高手的实战指南

在日常工作和学习中&#xff0c;PPT 是我们展示信息和进行演示的重要工具。无论是制作报告、演讲还是教学课件&#xff0c;PPT 都扮演着不可或缺的角色。然而&#xff0c;当面对大量重复性的 PPT 编辑任务时&#xff0c;手动操作不仅耗时耗力&#xff0c;还容易出错。幸运的是&…

系统设计中的幂等性

1. 基本概念 幂等性&#xff08;Idempotence&#xff09;是系统设计中经常提到的概念。如果某个操作执行一次或多次都能产生相同的结果&#xff0c;那么它就是幂等的。2. 代码示例 下面这段代码是幂等的。无论你调用多少次&#xff0c;show_my_button 的最终状态都是False。 de…

Pandas vs Polars Excel 数据加载对比报告

📊 Pandas vs Polars Excel 数据加载对比报告 1. 数据基本情况 数据文件:data.xlsx 数据规模:23,670 行 3 列 字段: case_time:日期/时间 case_name:公司名称(字符串) board:所属板块(字符串) 2. 加载方式与代码 Pandas import pandas as pdfrom tools import…

Kafka 为什么具有高吞吐量的特性?

Kafka 高吞吐量原因&#xff1a;面试题总结 在面试中&#xff0c;Kafka 的高吞吐量设计是高频考点&#xff0c;核心需围绕“架构设计”“存储优化”“网络效率”“资源利用”四个维度展开&#xff0c;以下是结构化总结&#xff1a; 一、核心架构&#xff1a;并行化与分层设计分…

MCP 协议原理与系统架构详解—从 Server 配置到 Client 应用

1. MCP MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;是开发 Claude 模型的(Anthropic)公司推出的一个开放标准协议&#xff0c;就像是一个 “通用插头” 或者 “USB 接口”&#xff0c;制定了统一的规范&#xff0c;不管是连接数据库、第三方…

uniapp安卓真机调试问题解决总结

uniapp安卓真机调试遇到各种连接不上问题&#xff1a; 手机上打开调试数据线不行&#xff0c;换数据线电脑重启手机重启拔出数据线&#xff0c;换个USB插口。

Linux Qt创建和调用so库的详细教程

一、创建so库1.文件-->新建文件或项目-->Library->C Library&#xff0c;如下图2.工程命名为Example3.一直下一步就可以4、工程创建完成&#xff0c;如下图5、删除Example_global.h6、配置.pro文件# 设置输出目录 DESTDIR $$PWD/output #只生成.so文件 CONFIG plugi…

【深度学习】蒙特卡罗方法:原理、应用与未来趋势

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 之前的文章参考下面的链接&#xf…

区块链技术原理(18)-以太坊共识机制

文章目录前言什么是共识&#xff1f;什么是共识机制&#xff1f;共识机制的核心目标共识机制的类型PoW&#xff08;工作量证明&#xff09;协议&#xff1a;&#xff08;2015-2022&#xff09;PoS&#xff08;权益证明&#xff09;协议&#xff1a;&#xff08;PoS&#xff0c;…

java基础(十五)计算机网络

网络模型概述 为了使得多种设备能通过网络相互通信&#xff0c;并解决各种不同设备在网络互联中的兼容性问题&#xff0c;国际标准化组织&#xff08;ISO&#xff09;制定了开放式系统互联通信参考模型&#xff08;OSI模型&#xff09;。与此同时&#xff0c;TCP/IP模型作为实际…