【多任务编程-线程通信】
进程/线程通信的方式
某些应用程序中,进程/进程和线程/线程之间不可避免的进行通信,进行消息传递,数据共享等
同一进程的线程之间通信方式包括Windows中常用Event, Message等。
不同进程之间的通信可以利用Event, FileMapping(内存共享), WM_COPYDATA消息以及ClipBoard(剪贴板),DDE, MessagePipe, MailSlot(邮件槽)等
在Tron系统中除了支持Event之外,还支持MailBox,MessageBuffer等通讯手段
使用事件进行通信
事件除了用来保证两个线程之间同步之外,借由通知功能,也可以作为线程之间的简单通信的手段
线程使用WaitForSingleObject等待一件事情的发生,该事情可以由另外一个线程通过SetEvent进行触发。
使用WaitForMultipleObjects等待多个由其他线程触发的事件(某些情况下,在线程的函数中也给自己发送消息
事件只能通知一件事情的发生,不能传递其他附属数据。
通知事件
调用SetEvent, 可以将事件的内核对象的状态变成 已通知
调用ResetEvent, 可以将事件的内核对象的状态变成 未通知
调用PulseEvent, 将事件对象置为有信号状态,然后立即置为无信号状态,在实际开发中这个函数很少使用
不同进程之间也可以通过Event通知事件,这可以通过命名对象来实现(CreateEvent时指定了名称,不同进程之间通过创建同名的Event就可以获得指向同一个内核对象的句柄,这样不同进程间就可以互相通知和等待Event
使用事件通知并传送数据
Event可以通知一件事情, 但是不能同时传送数据, 可以采用几种方式异步的获得数据.
先设置给接收方数据, 后通知Event.
由接收Event方提供设置(保存)数据的函数, 在接收方接受到通知后, 从自己保存的数据中取得数据.
先通知Event后从发送方取得数据
使用Event做成消息队列
在接收方做成一个存储消息和数据的队列, 每次登录一个消息(数据)后,通知接收方, 接受方从该队列中顺序取得数据.
Windows消息
Windows是一个消息(Message)驱动系统。Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。
Windows系统中有两种消息队列:系统消息队列和应用程序消息队列。计算机的所有输入设备由Windows监控。当一个事件发生时,Windows先将输入的消息放入系统消息队列中,再将消息拷贝到相应的应用程序消息队列中。应用程序的消息处理程序将反复检测消息队列,并把检测到的每个消息发送到相应的窗口函数中。这便是一个事件从发生至到达窗口函数必须经历的过程。
必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理
用户界面线程和工作线程
Windows中存在两种线程:一种是用户界面线程,一种是工作线程。
工作线程线程是用来执行某些辅助处理的线程,它不需要进行任何系统事件或者窗口事件的处理。
用户界面线程是指拥有自己的消息循环并能对用户界面对象进行创建、交互和撤销的线程。
Windows的消息机制与用户界面线程息息相关。用户界面线程一般是继承CWinThread类实现。
一个线程被创建后,系统假定线程不会被用于任何与用户相关的操作
一旦一个线程调用一个与图形用户界面有关的函数,(如创建窗口或者检查消息队列的函数,Windows会分配给这个线程一个THREADINFO结构。
线程的消息队列
每个线程利用THREADINFO来认为自己是在一个独占的环境中运行。在这个结构里保存了一系列的消息队列(登记消息队列、发送消息队列、应答消息队列)、唤醒标志、以及用来描述线程局部输入状态的若干变量。
THREADINFO结构是窗口消息系统的基础
当线程有了与之相联系的THREADINFO的结构时,线程就有了自己的消息队列集合
Windows消息结构介绍
消息结构如下:
typedef struct tagMSG
{ HWND hwnd ;
UINT message ;
WPARAM wParam ;
LPARAM lParam ;
DWORD time ;
POINT pt ;
}MSG, * PMSG ;
hwnd 接收消息的窗口句柄。
message 消息种别。这是一个数值,用以标识消息。对於每个消息息,均有一个对应的识别字,这些识别字定义在Windows头文件中(其中大多数在WINUSER.H中),以字首WM开头。
例如,使用者将鼠标窗口内,并按下左按钮,Windows就在消息队列中放入一个消息,该消息的message成员的值是WM_LBUTTONDOWN。这是一个常数,其值为0x0201。
wParam 一个32位的参数,其含义和数值根据消息的不同而不同。
lParam 一个32位的消息参数,其值与消息有关。
time消息放入消息队列中的时间。
pt消息放入队列时的鼠标座标。
用户使用自定义的消息的时候, 可以充分利用lParam,和 wParam传递数据
将消息发送到线程的消息队列中
通过PostMessage把消息发送给窗口所在的线程
PostMessage(HWND hWnd,UINT uMsg,
WPARAM wParam,LPARAM lParam)
-当一个线程调用了这个函数的时候,系统要确定时哪一个线程建立了用hWnd参数标志的窗口
-系统分配内存,存储消息参数,将这块内存增加到相应线程的消息队列中
-系统设置QS_POSTMESSAGE唤醒位标志
通过PostThreadMessage将消息放置在线程的消息队列
PostThreadMessage(DWORD dwThreadId,UINT uMsg,WPARAM wParam, LPARAM lParam)
-可以通过GetWindowThreadProcessID来确认是哪个线程创建了窗口
-ThreadID是在线程创建的时候获得的,在全系统范围内是唯一的
-要对线程编写消息循环(GetMessage,PeekMessage,DispatchMessage)
-PostQuitMessage(int nExitCode)向线程发送退出消息,等于PostThreadMessage(ThreadID,WM_QUIT,nExitCode,0);
PostMessage和SendMessage
当我们使用PostMessage时,是将一个Message复制到“登记消息队列”中,然后并立即返回。之后由GetMessage取回并响应之。
当我们使用SendMessage时,我们都知道,这个消息发送函数必须等到消息响应执行完毕才能返回。而它如何做到这一点的呢?当调用这个SendMessage的线程向这个线程自己创建的窗口发送消息的时候,它只是调用指定窗口的窗口过程,将其作为一个子例程,当窗口过程完成对消息的处理时,返回给SendMessage一个值。当一个线程向其他线程创建的窗口发送消息的时候,SendMessage首先将消息加入接收线程的“发送消息队列”,并为这个线程设置标志。同时发送线程将自己挂起,并在自己的“应答消息队列”中加入一个等待消息。当消息被接收线程处理完毕后,窗口的返回值被登记到发送线程的应答消息队列中。这是发送线程被唤醒,取出包含在应答消息队列中的返回值。
取得窗口消息
使用GetMessage或者PeekMessage取得窗口的消息
一个典型而普通的消息循环处理
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
这段处理必然实在用户界面线程的主循环中完成的
GetMessage的特点是当线程消息队列中有消息的时候则能立刻返回,否则进行等待
PeedMessage也能从线程消息队列中取得消息,它的特点是不管消息队列是否存在消息,都不等待。
CWinApp处理消息
MFC应用程序都有一个CWinApp对象,继承于CWinThread,在CWinApp的Run函数中进行了和GetMessage和DispatchMessage的处理,可以在MFC源代码THRDCORE.cpp找到CWinThread::Run()的代码,以及Run函数的运行时机。
同时等待消息和内核对象
某些情况下,你的线程不得不等待一个或多个事件,并且同时等待某些Message。
MsgWaitForMultipleObjects函数非常类似WaitForMultipleObjects,但它会在“对象被激发”或者”消息到达队列”时被唤醒并且返回
DWORD MsgWaitForMultipleObjects(
DWORD nCount, // 等待内核事件的个数
LPHANDLE pHandles, // 内核事件对象数组指针
BOOL fWaitAll, // 是否等待全部事件才返回
DWORD dwMilliseconds,// 超时值
DWORD dwWakeMask); // 唤醒Mask
MsgWaitForMultipleObjects
MsgWaitForMultiObjects的前四个参数和WaitForMultipleObjects完全相同
参数dwWakeMask指出了想要观察的用户输入Message
QS_ALLINPUT
QS_HOTKEY
QS_INPUT
QS_KEY
QS_MOUSE
QS_MOUSEBUTTON
QS_MOUSEMOVE
QS_PAINT
QS_POSTMESSAGE
QS_SENDMESSAGE
QS_TIMER
为了表达”消息到达队列“,返回值将是
WAIT_OBJECT_0+nCount
MsgWaitForMultipleObjects代码实例
DWORD WINAPI ThreadFunc(LPVOID lpParam)
{ bool bRun = true;
while(bRun) {
dwRet = MsgWaitForMultipleObjects(nEventCount, HandleArray, // event handle array
FALSE, // 只要有一个事件触发,就可以返回
dwMilliseconds, // 超时值
QS_ALLINPUT ); // 等全部消息
if (WAIT_FAILED == dwRet ) {// 错误处理}
if (WAIT_TIMEOUT==dwRet){// 超时处理}
if (WAIT_OBJECT_0+nEventCount==dwRet) {
while(PeekMessage(&Msg,NULL,0,0,PM_REMOVE) {
// 做消息处理
}
} else{
switch(dwRet-WAIT_OBJECT_0) {
case EVENT_EVT1:
// 处理事件1
break;
//...... 其它事件处理
}
}
}
}
Event和Message
Event会覆盖(丢失), Message不会
Event只能通知一件事情的发生,不能传送数据,Message可以传递简单的数据(通过LParam和WParam)
在同一个进程内, Message可以通过LParam或者WParam指向一个堆内存而达到数据通信的功能.
发Event需要指定目标Handle, 发Message需要指定目标Window(窗口句柄)
Event可以跨越进程, 通过名字达到共享.
Message也可以跨越进程, 通过注册消息(RegisterWindowMessage ), 在进程间共享消息.
进程间通信-自定义消息
在某些情况下我们可能需要向其他的应用程序发送消息,这时候我们可以采用SendMessage()函数向目标应用程序的某个窗口的句柄发送消息。其中的技巧在于获取该窗口的句柄。同时使用RegisterWindowMessage()函数创建一个唯一的消息,并且两个应用程序相互都了解这条消息的含义。同时还会用到BrodcastSystemMessage()函数,它可以向系统中的每个应用程序的主窗口发送消息。这样便可以避免出现获取另一个应用程序窗口句柄的问题。BroadcastSystemMessage()函数提供了附加的标志BSF_LPARAMPOINTER,可以将写入参数lParam的指针转化为可以被目标程序用来访问程序空间的指针,但是这个标志可能尚未进行文档标准化。
方法如下: 首先注册自己的窗口消息。不过我们这次不用WM_USER+1的技术,注册窗口消息的好处是不必费心考虑WM_USER加上某个数之后,所表示的消息标识符是否超出工程的允许范围。本例在两个工程中都使用文本字符串来注册消息。由于这个文本字符串在整个系统中应当是唯一的,因此将使用一种称为GUID的COM技术来命名消息。GUID名字生成器程序可以在MFC的\BIN目录下找到,其可执行文件名为GUIDGEN.EXE。该程序将生成在应用程序已知范围内认为是唯一的文本字符串,这对应用程序来说当然是最好不过的。
1) 注册一个唯一的窗口消息
使用GUIDGEN.EXE生成一个GUID。
在应用程序中把GUID定义为窗口消息文本字符串:#define HELLO_MSG “{6047CCB1-E4E7-11d1-9B7E-00AA003D8695}”
使用::RegisterWindowsMessage()注册该窗口消息文本字符串:idHelloMsg = ::RegisterWindowMessage( HELLO_MSG );
保存消息标识符idHelloMsg,便于以后使用。
2) 向其他应用程序发送消息
使用::RegisterWindowsMessage()返回的消息标识符发送消息,可使用以下代码:
::SendMessage(hWnd, idHelloMsg,wParam,lParam);
以上代码假定事先可以通过某种方式获取目标应用程序的某个窗口的句柄。一个指向CWnd类的指针不能在程序范围之外而发挥作用。但是可以在CWnd 类中封装已获取的窗口句柄,并如下所示来发送消息:
CWnd wnd;
wnd.Attach( hWnd );
wnd.SendMessage( idHelloMsg,wParam,lParam );
3) 接收已注册的窗口消息
为接收已注册的窗口消息,需要在接收窗口类,一般为CMainFrame中手工添加ON_REGISTERED_MESSAGE消息宏到消息映射中:
BEGIN_MESSAGE_MAP( CMainFrame, CMDIFrameWnd )
// {{AFX_MSG_MAP( CMainFrame )
// }}AFX_MSG_MAP
ON_REGISTERED_MESSAGE( idHelloMsg,OnHelloMsg )
END_MESSAGE_MAP()
有关已注册消息的消息处理函数的代码如下:
LRESULT CMainFrame::OnHelloMsg( WPARAM wParam,LPARAM lParam )
{
// process message
return 0;
}
该实例到目前为止,一直假定事先可以通过某种方式取得目标应用程序的某个窗口的句柄。但这是一个困难的任务。简单的方法是向每个应用程序广播一条消息,并且希望目标程序正在监听。由于在系统中注册了一条唯一的消息,因此只有目标程序会响应这条消息。应用程序广播的消息可能是它自己的窗口句柄,于是接收程序可以使用::SendMessage()来发送应答,也可能是用窗口句柄来结束循环。
4) 广播窗口消息
使用下面的代码广播窗口消息:
WPARAM wParam = xxx;
LPARAM lParam = xxx;
DWORD dwRecipients = BSM_APPLICATIONS;
::BroadcastSystemMessage( BSF_IGNORECURRENTTASK,&dwRecipients,idHelloMsg,wParam,lParam );
进程间通信-WM_COPYDATA
通过SendMessage可以把一个消息发送到另外一个进程,但是在另外一个进程中试图访问LPARAM所指向的数据,也许会发生错误
进程地址空间是受到保护和相互隔离的,视图访问另外一个进程的地址空间是不正确的。
Windows定义了WM_COPYDATA消息专门用来在线程之间传递数据,不管两个线程是否属于一个进程
处理WM_COPYDATA的线程必须有消息队列和窗口(带有消息队列的工作线程或者UI主线程)
SendMessage(hWndReceiver,
WM_COPYDATA,
(WPARAM)hWndSender,
(LPARAM)&cds);
LPARAM 参数cds指向一个特定的Windows数据结构COPYDATASTRUCT
typedef struct tagCOPYDATASTRUCT{
DWORD dwData;//通常用户自定义为行动代码
DWORD cbData;// lpData所指的数据块大小
PVOID lpData; // 一块数据,可以被传送到接受端
}COPYDATASTRUCT,*PCOPYDATASTRUCT;
必须使用SendMessage而不是PostMessage发送WM_COPYDATA消息
WM_COPYDATA所传送的数据可以在Heap上也可以在Stack上申请,因为SendMessage保证接收方在返回前完成对数据的操作。
接受方必须在WM_COPYDATA消息处理函数中完成对lpData的处理(读取和保存其中的内容),而不是保存lpData指针,对接收方来说,在这次消息处理后,lpData所指向的内存将不再是可用的。
进程间通信-共享内存(SharedMemory)
Win32进程之间有严密的保护,一个进程要看到另外一个进程的地址空间中的任何一部分,都是不可能的。
程序只能见到逻辑的地址,所有进程的逻辑地址空间都是相同的(4GB的理论地址空间)
WM_COPYDATA技术简单但是效率不高
使用Win32进程通信技术的最低层:共享内存(SharedMemory)来进行高效的进程间数据共享
使用共享内存的方法
1.设定一块儿共享内存
2.使用共享内存
3.同步处理共享内存
设定共享内存块
1. 产生一个FileMapping内核对象,指定共享区域大小(CreateFileMapping)
2. 将共享区域映射到你的进程的地址空间
(MapViewOfFile)
找到共享内存块
有些情况下,由Server进程创建一个共享内存,其他进程只需要找到这块共享内存并使用就可以了。
1. 找到一个FileMapping内核对象,使用OpenFileMapping
系统找到同一个FileMapping内核对象是根据 对象的命名
2. 将共享区域映射到你的进程的地址空间 (MapViewOfFile)
同步使用共享内存
多个进程使用共享内存的时候,必须保证同步安全的使用共享的内存,通常使用等待被命名的Mutex来保证。
创建共享内存CreateFileMapping
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全设置
DWORD flProtect, //保护设置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
);
1) 物理文件句柄
任何可以获得的物理文件句柄, 如果你需要创建一个物理文件无关的内存映射也无妨, 将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了.
如果需要和物理文件关联, 要确保你的物理文件创建的时候的访问模式和“保护设置”匹配, 比如: 物理文件只读, 内存映射需要读写就会发生错误. 推荐你的物理文件使用独占方式创建.
如果使用 INVALID_HANDLE_VALUE, 也需要设置需要申请的内存空间的大小, 无论物理文件句柄参数是否有效, 这样 CreateFileMapping 就可以创建一个和物理文件大小无关的内存空间给你, 甚至超过实际文件大小, 如果你的物理文件有效, 而大小参数为0, 则返回给你的是一个和物理文件大小一样的内存空间地址范围. 返回给你的文件映射地址空间是可以通过复制, 集成或者命名得到, 初始内容为0.
2) 保护设置
就是安全设置, 不过一般设置NULL就可以了, 使用默认的安全配置. 在win2k下如果需要进行限制, 这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是, 可以考虑进行限制.
3) 高位文件大小
目前我们的机器都是32位的, 不可能得到超过32位进程所能寻址的私有32位地址空间, 一般还是设置0.
4) 低位文件大小
实际共享内存的大小, 不过为了让其他共享用户知道你申请的文件映射的相关信息, 使用的时候是在获得的地址空间头部添加一个结构化描述信息, 记录内存映射的大小, 名称等。
5) 共享内存名称
在其他进程创建同名的共享内存的时候,系统会返回存在的共享内存.
映射共享内存-MapViewOfFile
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
MapViewOfFile()函数负责把文件数据映射到进程的地址空间。
hFileMappingObject为CreateFileMapping()返回的文件映像对象句柄。dwDesiredAccess则再次指定了对文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
MapViewOfFile()函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:
SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;
MapViewOfFile的返回值就是被映射在进程地址空间中的共享内存块指针,用户操作这个指针可以达到共享内存了。
使用共享内存-Create/OpenFileMapping
共享内存以点对点(peer to peer)的形式呈现
则每个进程都必须有相同的能力,产生共享内存并将它初始化。每个进程 都应该调用CreateFileMapping(),然后调用GetLastError().如果传回的错误代码是ERROR_ALREADY_EXISTS,
那么进程就可以假设这一共享内存区域已经被别的进程打开并初始化了,否则该进程就可以合理的认为自己 排在第一位,并接下来将共享内存初始化。
共享内存以client/server架构的形式呈现
只有server进程才应该产生并初始化共享内存。所有的进程都应该使用由OpenFileMapping得到的共享内存映射文件。
HANDLE OpenFileMapping(DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
其中lpName是其他进程用CreateFileMapping函数创建共享内存时时指定的对象名称。
再调用MapViewOfFile(),取得共享内存的指针
相关文章:

【多任务编程-线程通信】
进程/线程通信的方式 某些应用程序中,进程/进程和线程/线程之间不可避免的进行通信,进行消息传递,数据共享等 同一进程的线程之间通信方式包括Windows中常用Event, Message等。 不同进程之间的通信可以利用Event, FileMapping(内存共享), W…...

K8S暴露pod内多个端口
K8S暴露pod内多个端口 一、背景 公司统一用的某个底包跑jar服务,只暴露了8080端口 二、需求 由于有些服务在启动jar服务后,会启动多个端口,除了8080端口,还有别的端口需要暴露,我这里就还需要暴露9999端口。 注&a…...

Python 列表
""" #list函数 ls list() #创建一个空列表 print(list()) print(list(str(1234)))#[1, 2, 3, 4] print(list(range(5)))#[0, 1, 2, 3, 4] print(list((1,2,3,4)))#[1, 2, 3, 4] print(list(Lift is short, you need python))#注意空格也算一个字符 #[L, i, f,…...

Rabbitmq的安装与使用(Linux版)
目录 Rabbitmq安装 1.在Ubuntu上安装RabbitMQ: 打开终端,运行以下命令以更新软件包列表: 安装RabbitMQ: 安装完成后,RabbitMQ服务会自动启动。你可以使用以下命令来检查RabbitMQ服务状态: 2.在CentOS…...

Kubernetes对象深入学习之四:对象属性编码实战
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习》系列的第四篇,前面咱们读源码和文档,从理论上学习了kubernetes的对象相关的知识ÿ…...

深度学习入门教程(2):使用预训练模型来文字生成图片TextToImageGenerationWithNetwork
本深度学习入门教程是在polyu HPCStudio 启发以及资源支持下进行的,在此也感谢polyu以及提供支持的老师。 本文内容:在GoogleColab平台上使用预训练模型来文字生成图片Text To Image Generation With Network (1)你会学到什么&a…...

ORA-38760: This database instance failed to turn on flashback database
早晨接一个任务,使用rman备份在虚拟化单机上恢复实例,恢复参数文件、控制文件和数据文件都正常,recover归档时报错如下: Starting recover at 2023-07-28 10:25:01 using channel ORA_DISK_1 starting media recovery media reco…...

避免低级错误:深入解析Java的ConcurrentModificationException异常
在软件开发中,我们常常会遇到各种错误和异常。其中有一类比较低级但又常见的错误就是ConcurrentModificationException异常。最近了我就写了个这种异常,这个异常通常发生在使用迭代器遍历集合时,同时对集合进行修改,从而导致迭代器…...

7.28
1.思维导图 2.qt的sever #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器类 #include<QTcpSocket> //客户端类 #include<QMessageBox> //对话框类 #include<QList> …...

java线程中的常见方法(详解)
方法简介 方法名 功能 说明 start() 启动一个新线程,在新的线程运行 run 方法中的代码 start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如…...

线程池参数配置
上次面试被人问到,如果是IO 密集型的任务,该如何配置合适的线程数,当初我说要按照IO具体的请求毫秒时间,来配置具体的线程数。 NthreadsNcpu*(1w/c) 公式中 W/C 为系统 阻塞率 w:等待时间 c:计算时间一般情况下,如果存…...

Spread for Winform 16.2.20231.0 (SP2) Crack
Spread for Winform 16.2.20231.0 (SP2)发布。此版本包含针对客户报告的问题的重要修复: 安装版本 16 后,FarPoint.Localization.dll 将丢失。 将数据绑定到 Spread 时会出现 InvalidOperationException。 通过 Spread Designer 设置的上标将不会保留。…...

Go程序结构
Go程序结构 1、名称 名称的开头是一个字母或下划线,且区分大小写。 实体第一个字母的大小写决定其可见性是否跨包: 若名称以大写字母开头,它是导出的,对包外是可见和可访问的,可以被自己包以外的其他程序所引用…...

JAVA面试总结-Redis篇章(四)——双写一致性
JAVA面试总结-Redis篇章(四)——双写一致性 问:redis 做为缓存,mysql的数据如何与redis进行同步呢?第一种情况,如果你的项目一致性要求高的话 采用以下逻辑我们应该先删除缓存,再修改数据库&…...

赋能医院数字化转型,医院拍摄VR全景很有必要
医院有没有必要拍摄制作VR全景呢?近期也有合作商问我们这个问题,其实VR智慧医院是趋势、也是机遇。现在外面很多的口腔医院、医美机构等都开始引入VR全景技术了,力求打造沉浸式、交互式的VR智慧医院新体验,通过VR全景展示技术来助…...

Vue3项目中没有配置 TypeScript 支持,使用 TypeScript 语法
1.安装 TypeScript:首先,需要在项目中安装 TypeScript。在终端中运行以下命令 npm install typescript --save-dev2.创建 TypeScript 文件:在 Vue 3 项目中,可以创建一个以 .ts 后缀的文件,例如 MyComponent.ts。在这…...

数据可视化大屏拼接屏开发实录:屏幕分辨率测试工具
一、可视化大屏开发 在数据可视化大屏开发时,确定数据可视化大屏拼接屏的板块尺寸需要考虑以下几个因素: 屏幕分辨率:首先需要知道每个板块屏幕的分辨率,包括宽度和高度,这决定了每个板块上可以显示的像素数量。 数据…...

每日一题7.28 209
209. 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 本题应该是用前缀…...

Python + Playwright 无头浏览器Chrome找不到元素
用Python Playwright调试时,发现不用无头浏览器(即headlessFalse)代码能够运行成功,但是一用无头浏览器时(即headlessTrue)就会报错,提示找不到元素。换成Firefox浏览器又不会有这个问题&#…...

C++信号量与共享内存实现进程间通信
关于信号量和共享内存的相关知识可参考下面链接: 进程间通信方式介绍_夜雨听萧瑟的博客-CSDN博客 C 创建共享内存_c共享内存_夜雨听萧瑟的博客-CSDN博客 信号量SytemV与Posix信号量的介绍与用法_夜雨听萧瑟的博客-CSDN博客 直接上代码,代码如下&#…...

[Tools: Camera Conventions] NeRF中的相机矩阵估计
参考:NeRF代码解读-相机参数与坐标系变换 - 知乎 在NeRF中,一个重要的步骤是确定射线(rays)的初始点和方向。根据射线的初始点和方向,和设定射线深度和采样点数量,可以估计该射线成像的像素值。估计得到的…...
【sgUpload】自定义上传组件,支持上传文件夹及其子文件夹文件、批量上传,批量上传会有右下角上传托盘出现,支持本地上传图片转换为Base64image
特性: 支持批量上传文件、文件夹可自定义headers可自定义过滤上传格式可自定义上传API接口支持drag属性开启可拖拽上传文件、文件夹 sgUpload源码 <template><div :class"$options.name" :dragenter"isDragenter"><!-- 上传按钮…...

Kafka 实时处理Stream与Batch的对比分析
Kafka 实时处理Stream与Batch的对比分析 一、简介1. Kafka的定义和特点2. Kafka实时处理基础架构 二、Stream和Batch1. Stream和Batch的区别2. 对比Stream和Batch的优缺点Stream的优缺点Batch的优缺点 三、使用场景1. 使用场景对比Batch使用场景Stream使用场景 2. 如何选择Stre…...

Andriod开发性能优化实践
文章目录 内存优化布局优化网络优化图片优化内存泄露绘制优化 内存优化 在Android开发中,有一些实践可以帮助进行内存优化,以减少应用程序的内存占用和提高性能。以下是一些常见的内存优化实践: 使用合适的数据结构和集合:选择合…...

linux环境安装mysql数据库
一:查看是否自带mariadb数据库 命令:rpm -qa | grep mariadb 如果自带数据库则卸载掉重新安装 命令:yum remove mariadb-connector-c-3.1.11-2.el8_3.x86_64 二:将压缩文件上传到/user/local/mysql文件夹 或者直接下载 命令&a…...

【深度学习中常见的优化器总结】SGD+Adagrad+RMSprop+Adam优化算法总结及代码实现
文章目录 一、SGD,随机梯度下降1.1、算法详解1)MBSGD(Mini-batch Stochastic Gradient Descent)2)动量法:momentum3)NAG(Nesterov accelerated gradient)4)权重衰减项(we…...

山东大学软件学院考试回忆——大二上
文章目录 学习科目整体回忆上课考试回忆Web技术大学物理概率与统计计算机组织与结构离散数学(2)数据结构(双语) 学习科目 Web技术大学物理概率与统计计算机组织与结构离散数学(2)(双语…...

【Express.js】异常分类和日志分级
异常分类和日志分级 第一章已经介绍过全局的异常处理了,但之前的做法过于简单,一股脑的捕获并返回。这一节我们将对异常进行细致的分类,并且日志也做标准化的分级。 准备工作 一个基础的 evp-express 项目 NodeJS Error 先了解一下 Node…...

k8s webhook实例,java springboot程序实现 对Pod创建请求添加边车容器 ,模拟istio实现日志文件清理
k8s webhook实例,java springboot程序实现 对Pod创建请求添加边车容器 ,模拟istio实现日志文件清理 大纲 背景与原理实现流程开发部署my-docker-demo-sp-user服务模拟业务项目开发部署my-sidecar服务模拟边车程序开发部署服务my-docker-demo-k8s-opera…...

关于electron的问题汇总
1. electron-builder打包慢出错的问题 由于网络原因,在进行builder打包时,可能会等很长时间,直到最后还是以失败告终。 如果是第一次进行builder打包,会去下载winCodeSign、nsis、nsis-resources,往往都是第一个就卡住…...