记录一次WPF程序进程挂起问题
## 1. 使用背景
开发`WPF`单进程项目,在项目中使用`MongoDB`数据库,需要连接多个不同的数据库实例,另外项目框架采用了事件聚合器来管理模块间的通知调用,基于`NetMQ`实现了一个`ZeroMQPublisher`和`ZeroMQSubscriber`。
**事件聚合器服务实现方案**:
* `ZeroMQPublisher` 启动时会监听本机地址的一个端口(比如`tcp://*:5866`)
* `ZeroMQSubscriber`启动时需要去连接NetMQ服务端(比如:`tcp://127.0.0.1:5866`)
**启动数据库实例的实现方案**:
在`C#`代码中通过`Process`来启动`mongod.exe`,启动时分别指定不同的启动参数(`--dbpath` `--port` `--replSet` `--logpath` 等等)。该逻辑在应用程序启动时调用。
* 具体实现代码片段如下:
<!---->
var startInfo = new ProcessStartInfo
{
FileName = processName,
Arguments = $"--dbpath \"{mongoConfig.DBPath}\" --port {mongoConfig.Port} --replSet {mongoConfig.ReplicaSetName} " +
$"--logpath {Path.Combine(mongoConfig.DBPath, "mongod.log")} --logappend",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
// 指定用于读取标准输出流的编码。指定为 UTF8 可以确保正确读取输出
StandardOutputEncoding = Encoding.UTF8,
// 指定用于读取标准错误流的编码。同样,指定为 UTF8 可以确保正确读取错误输出
StandardErrorEncoding = Encoding.UTF8
};
var _mongoProcess = new Process { StartInfo = startInfo };
_mongoProcess.Start();
// 订阅输出和错误事件
_mongoProcess.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
outputMsgCallback?.Invoke("MongoDB 输出: " + e.Data);
}
};
_mongoProcess.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
outputMsgCallback?.Invoke("MongoDB 错误: " + e.Data);
}
};
_mongoProcess.BeginOutputReadLine();
_mongoProcess.BeginErrorReadLine();
注:为了实现数据库实例在`WPF`程序退出后依然可以连接,故在退出程序时,未清除上面的`Process`资源。
* 实现效果:
* a. 可以通过配置启动多个不同的`mongo db` 数据库实例;
* b. 可以将`mongo db`数据库实例启动的日志重定向输出,并通过`outputMsgCallback`的定义来记录起来(已有指定的 --logpath ,其实可以不需要重定向的输出信息)
## 2. 问题现象
* `WPF`程序退出后,再次启动程序,会提示如下报错信息:
<!---->
内部异常:
SocketException: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
* 检查端口占用情况,执行 `netstat -ano | finstr :5866`,返回如下信息:
<!---->
TCP 0.0.0.0:5866 0.0.0.0:0 LISTENING 46064
TCP 127.0.0.1:3172 127.0.0.1:5866 ESTABLISHED 46064
TCP 127.0.0.1:5866 127.0.0.1:3172 ESTABLISHED 46064
通过上面显示的进程`id 46064` 去查询对应的进程(`tasklist /FI "PID eq 46064" `),显示查询不到对应的进程信息。

## 3. 问题原因及分析
1. 检查实现逻辑,发现在应用程序退出时,已调用对应的`NetMQ`的`Dispose`释放逻辑;
2. 优化尝试:在释放连接资源前,先执行`DiscConnect`逻辑亦无法解决问题;
3. 工具分析 `[TCPView](https://learn.microsoft.com/en-us/sysinternals/downloads/tcpview?spm=5aebb161.543df828.0.0.737f7038lsFU2W)`
使用`TCPView`工具可以看到上面对应的3个`tcp`的连接信息,但是无法查看到关联的实际进程信息,依然无法解决问题。
4\. 使用`[ProcessExplorer](https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer)`工具查看进程信息,选择`mongod.exe`,查看属性,发现其会显示`Parent`为上面的WPF应用进程。


`kill`这两个`mongod.exe`后,不再出现上面的端口占用问题。因此,问题的主要原因是`mongod.exe`启动的数据库实例进程和主进程是关联在一起的,根本原因是`Process`启动进程是未和主进程相隔离。
## 4. 解决方案
1. 修改进程启动方式:
<!---->
var startInfo = new ProcessStartInfo
{
FileName = processName,
Arguments = $"--dbpath \"{mongoConfig.DBPath}\" --port {mongoConfig.Port} --replSet {mongoConfig.ReplicaSetName} " +
$"--logpath {Path.Combine(mongoConfig.DBPath, "mongod.log")} --logappend",
UseShellExecute = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
};
* 通过`Shell`来启动进程(`UseShellExecute = true`),而不是直接由当前应用程序启动;
* 隐藏启动的`Shell`窗口(`WindowStyle = ProcessWindowStyle.Hidden`);
记录一次WPF程序进程挂起问题的更多相关文章
- c# 守护进程,WPF程序自守护
原文:c# 守护进程,WPF程序自守护 版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/lwwl12/article/details/79035246 如何 ...
- WPF 程序如何跨窗口/跨进程设置控件焦点
原文:WPF 程序如何跨窗口/跨进程设置控件焦点 WPF 程序提供了 Focus 方法和 TraversalRequest 来在 WPF 焦点范围内转移焦点.但如果 WPF 窗口中嵌入了其他框架的 U ...
- WPF 程序如何移动焦点到其他控件
原文:WPF 程序如何移动焦点到其他控件 WPF 中可以使用 UIElement.Focus() 将焦点设置到某个特定的控件,也可以使用 TraversalRequest 仅仅移动焦点.本文介绍如何在 ...
- Linux C 程序 进程控制(17)
进程控制 1.进程概述现代操作系统的特点在于程序的并行执行.Linux是一个多用户多任务的操作系统.ps .pstree 查看进程进程除了进程id外还有一些其他标识信息,可以通过相应的函数获得.// ...
- WPF 程序中启动和关闭外部.exe程序
当需要在WPF程序启动时,启动另一外部程序(.exe程序)时,可以按照下面的例子来: C#后台代码如下: using System; using System.Collections.Generic; ...
- [Python 学习]2.5版yield之学习心得 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source …
[Python 学习]2.5版yield之学习心得 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source - [Pyth ...
- Winform 程序嵌入WPF程序 并发送消息
废话不多说,先看解决方案目录 WindowsFormsDemo是主程序,WpfApp是嵌入的WPF程序,先看WPF程序,程序默认启动的页面是MainWindow.xaml,这里注释掉App.xaml里 ...
- WPF 一个空的 WPF 程序有多少个窗口
原文:WPF 一个空的 WPF 程序有多少个窗口 好多小伙伴说 WPF 的程序有五个窗口,但是我尝试使用了 EnumThreadWindows 去获取的时候居然拿到了 10 多个窗口 在 WPF 内部 ...
- 进程 PCB 进程挂起
7-1 进程定义 OS系统从只能跑一个程序到能跑多个.进程可以描述程序的执行过程. 进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程. 只有当一个程序被OS加载到内存中,cpu对其 ...
- 在WPF程序中打开网页:使用代理服务器并可进行JS交互
本项目环境:使用VS2010(C#)编写的WPF程序,通过CefSharp在程序的窗体中打开网页.需要能够实现网页后台JS代码中调用的方法,从网页接收数据,并能返回数据给网页.运行程序的电脑不允许上网 ...
随机推荐
- Asp.net MVC中的Http管道事件为什么要以Application_开头?
今天遇到一个问题,需要在API请求结束时,释放数据库链接,避免连接池被爆掉. 按照以往的经验,需要实现IHttpModule,具体不展开了. 但是实现了IHttpModule后,还得去web.conf ...
- HarmonyOS Next 入门实战 - 创建项目、主题适配
开发一个简单的demo,其中涉及一些鸿蒙应用开发的知识点,其中涉及导航框架,常用组件,列表懒加载,动画,深色模式适配,关系型数据库等内容,在实践中学习和熟悉鸿蒙应用开发. 首先下载并安装 ...
- uni app 封装接api接口
创建文件 base.js let baseURL = ''; // 是否在控制台显示接口请求日志,本地环境启用,打包环境禁用 let showHttpLog = false; // 测试环境 bas ...
- 禁用SAP Hana错误密码锁定用户功能
背景 公司项目适配多种数据库其中包含SAP Hana,由于有同事的数据库连接工具保存了某个在用的数据库的旧密码,导致时不时会被锁用户.通过查询官方文档已解决,这里统一记录一下. 禁用密码锁定方法 以下 ...
- VLC web(http)控制 (4) 服务器文件获取
通过链接 http://127.0.0.1:8080/requests/browse.xml?uri=file%3A%2F%2F~ 可以获取服务器默认目录所有文件. 其中file%3A%2F%2F~是 ...
- 【前缀和+开区间二分】codeforces 1187 B. Letters Shop
题意 第一行,输入一个正整数 \(n(1 \leq n \leq 2*10^5)\),代表字符串 \(s\) 的长度. 第二行,输入一个字符串 \(s\). 第三行,输入一个正整数 \(m(1 \le ...
- Centos使用图形化界面配置网络
1. 查看当前ip地址 # ip addr 2. 图形化界面配置网卡 # nmtui 界面提示,左右上下配置,OK即可.
- IDEA利用阿里云插件部署Springboot项目
下载插件 搜索 Alibaba Cloud Toolkit 插件,并安装. IDEA增加Run/Debug Configurations Add New Configuration - Deploy ...
- [转]IDEA2020.2.3中创建JavaWeb工程的完整步骤记录
原文链接: IDEA2020.2.3中创建JavaWeb工程的完整步骤记录
- 网络编程入门如此简单(四):一文搞懂localhost和127.0.0.1
本文由萤火架构分享,原题"localhost和127.0.0.1的区别是什么?",原文链接"juejin.cn/post/7321049446443417638" ...