知识讲解:
线程共享内存优点
-
高效性:线程内共享内存是在同一进程的不同线程之间进行数据共享,避免了进程间通信的开销和复杂性,因此访问速度更快。一个程序最少有一个进程,而一个进程可以有多个线程。
-
简单性:相比于其他形式的数据共享方式(如管道、消息队列等),线程内共享内存使用起来更加简单直接,不需要额外的通信机制(例如:TCP,udp等传输协议)。
-
实时性:由于线程内共享内存无需经过操作系统或其他中间介质,在同一进程中的各个线程可以直接读写该共享内存区域,从而达到较高的实时性。
实现原理:
共享内存的实现原理涉及虚拟内存和物理内存的概念以及操作系统的支持。
1. 虚拟内存和物理内存:在现代操作系统中,每个进程都有自己的虚拟内存空间,它是一个抽象的地址空间。虚拟内存空间由连续的虚拟地址组成,而实际的数据存储在物理内存中。操作系统负责将虚拟地址映射到物理内存中的对应地址。
2. 共享内存的实现:共享内存是在虚拟内存中创建的一块特殊区域,它被多个进程或线程映射到各自的虚拟地址空间中。这意味着多个进程或线程可以通过它们各自的虚拟地址来访问同一块物理内存,实现数据的共享。
3. 操作系统支持:操作系统会提供一些特定的系统调用,允许进程或线程创建共享内存区域,并将其映射到虚拟内存空间中。
4. 共享内存的创建和映射:在使用共享内存之前,进程或线程需要通过系统调用创建共享内存区域,并获得一个唯一的标识符,即句柄。此后,它们可以通过系统调用将这个共享内存区域映射到各自的虚拟地址空间中的某个地址,之后通过拷贝该地址的内容进行打印输出。
5. 数据的读写和同步:一旦共享内存映射完成,进程或线程可以通过访问共享内存中的数据来进行读写操作。
6. 物理内存的管理:物理内存是实际存储数据的地方。操作系统负责管理物理内存的分配和释放,以满足进程或线程的共享内存需求。当进程或线程创建共享内存时,操作系统会为其分配一块物理内存,并将其映射到所有相关的进程或线程的虚拟地址空间中。
总之,共享内存是通过将一块物理内存映射到多个进程或线程的虚拟地址空间中实现的。通过操作系统提供的系统调用和机制,进程或线程可以创建共享内存区域并映射到自己的虚拟地址空间中。这样,它们可以直接访问共享内存中的数据进行读写操作,通过适当的同步机制保证数据的正确和一致。操作系统负责管理物理内存的分配和释放,以满足共享内存的需求
应用场景:
- 数据缓冲区:需要多个线程共享一个数据缓冲区,可以实现高效的数据传输和处理。
- 共享状态信息:多个线程需要访问和更新某个共享的状态信息,例如计数器或标志位。
- 线程间通信:通过共享内存进行线程间通信,避免了进程间通信的开销和复杂性。
代码实现
代码(服务器端):
#define _CRT_SECURE_NO_WARNINGS
//防止系统报错strcpy,要加在头文件前
#include <iostream>
#include <Windows.h>
using namespace std;
#define BUF_SIZE 1024 //这种变量后面不可以加分号结尾
int main() {
//创建需要共享的信息
char szBuffer[] = "我的实战项目经验";
//创建共享文件句柄,最后一个是共享文件名字
//代码分析1
HANDLE hmFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, BUF_SIZE, L"vico");
//对于上行代码,HANDLE 是创建句柄类名,hmFile 是创建的对象名。
//下面映射
//代码汾西2
LPVOID lpBase = MapViewOfFile(hmFile, FILE_MAP_ALL_ACCESS,
0, 0, BUF_SIZE);
//将数据拷贝到共享内存中。
strcpy((char*)lpBase, szBuffer);
cout << "\n服务器程序端:" << (char*)lpBase << endl << endl;
//线程挂起等待其他线程读取数据
Sleep(20000); //这里让系统暂停20秒,
//删除文件映射
UnmapViewOfFile(lpBase);
//关闭内存映射文件对象句柄
CloseHandle(hmFile);
return 0;
}
分析(服务器端):
适用于进程间的相互通信。其中代码分析:
分析1:
HANDLE hmFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, L"vico");
:创建一个共享文件映射对象,并返回其句柄。参数依次表示无效文件句柄、安全属性、页面保护模式、共享内存大小、共享文件名字。这里将共享文件命名为"vico"。HANDLE 创建句柄。
分析2:
LPVOID lpBase = MapViewOfFile(hmFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
:将共享文件映射到当前进程的地址空间,并返回指向共享内存的指针。参数依次表示共享文件句柄、访问权限、偏移量(从文件开头开始)、视图起始偏移和视图大小。
代码(客户端):
#define _CRT_SECURE_NO_WARNINGS //禁止编译器报strcpy错误
#include <iostream>
#include <Windows.h>
using namespace std;
#define BUF_SIZE 1024 //定义一个无参宏,后不加分号。
int main() {
//打开文件共享,共享文件名字
HANDLE hmfile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
NULL, L"vico"); //在线程中搜索共享句柄
if (hmfile) //搜索到句柄
{
LPVOID lpBase = MapViewOfFile(hmfile,
FILE_MAP_ALL_ACCESS, 0, 0, 0);
//找到并定义指向共享内存的指针
char szBuffer[BUF_SIZE] = { 0 };
//定义一个空字符串
strcpy(szBuffer, (char*)lpBase); //复制字符串
cout << "\n客户端程序端:" << szBuffer;
//删除文件映射,释放内存
UnmapViewOfFile(lpBase);
CloseHandle(hmfile);
}
else { //打开失败
printf("\n打开共享内存失败,请检查操作。");
}
return 0;
}
分析(客户端):
HANDLE hmfile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, L"vico");
:打开共享文件映射对象。OpenFileMapping函数返回一个句柄(handle),表示对共享内存的访问权限。具体参数意义如下:
第一个参数FILE_MAP_ALL_ACCESS表示以可读写方式打开文件映射。
第二个参数NULL表示默认安全性选项。
第三个参数L"vico"是共享文件映射对象的名称,在此处假设其名称为"vico"
LPVOID lpBase = MapViewOfFile(hmfile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
将共享内存映射到进程的地址空间中。MapViewOfFile函数返回一个指向共享内存的指针,具体参数意义如下:
第一个参数hmfile是要映射的共享内存对象的句柄。
第二个参数FILE_MAP_ALL_ACCESS表示以可读写方式进行映射。
后面三个参数为偏移量、映射视图的大小和文件偏移量,在此处都设置为0,表示从头开始映射整个共享内存
lpBase即为句柄的指针,程序后面强制转换为char*类型指针,最后拷贝给char类型变量,至此句柄的读写结束,最后别忘了进行资源的关闭。