程序在开发电脑上能跑,客户机器上就崩溃,那是你还没意识到是它?

程序开发之谜:为何在开发电脑上一切正常,一到客户机器就崩溃?


1. 引言:

在软件开发的世界里,没有什么比看到自己精心编写的程序在自己的开发环境中完美运行,却在客户机器上突然崩溃更让人抓狂的事情了。这种现象看似诡异,实则背后往往隐藏着一系列潜在的原因,从硬件差异到软件冲突,甚至是系统设置的不同。今天,我们将揭开这个谜团,探讨如何使用Windows资源监视器来诊断并解决此类问题。

2. 莫慌,这个问题具有普遍性

每当在软件开发遇到这种情况时,通常会经历三个阶段:第一阶段是困惑,第二阶段是调查,第三阶段是解决问题。困惑源于预期与现实之间的巨大落差;调查则需要开发者转变角色,从程序员变成侦探;而解决问题,则是对开发者技能和耐心的双重考验。

3 .使用Windows资源监视器进行诊断

Windows资源监视器是Windows系统自带的一个强大工具,它提供了关于CPU、内存、磁盘和网络使用情况的实时数据。以下是使用资源监视器进行诊断的步骤:

步骤0:编写一个WPF程序

编码文件 MainWindow.xaml

<Window x:Class="WPFSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFSample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Hello, WPF!" FontSize="32" Margin="0,20,0,20" />
            <Label x:Name="cpuTimeLabel" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="32" Margin="0,20,0,20"/>
            <Button HorizontalAlignment="Center" x:Name="myButton" Click="myButton_Click" Margin="0,0,0,20">Click Me</Button>
            <RichTextBox Name="consoleTextBox" Height="250"></RichTextBox>
        </StackPanel>
        
    </Grid>
</Window>

编码文件 MainWindow.xaml.cs

using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows;
using System.Windows.Threading;

namespace WPFSample
{
    public partial class MainWindow : Window
    {
        private Process _currentProcess;
        private DispatcherTimer _timer;

        private const int Port = 8888;
        private TcpListener _listener;
        private TcpClient _ServerTcpClient;
        private TcpClient _ClientTcpClient;

        public MainWindow()
        {
            InitializeComponent();
            // 获取当前进程
            _currentProcess = Process.GetCurrentProcess();

            // 创建一个每秒钟触发一次的定时器
            _timer = new DispatcherTimer();
            _timer.Interval = TimeSpan.FromMicroseconds(100);
            _timer.Tick += Timer_Tick;
            _timer.Start();
        }

        private void myButton_Click(object sender, RoutedEventArgs e)
        {
            (sender as System.Windows.Controls.Button).Content = "Clicked!";
            StartServer();
            StartClient();
        }
        private void Timer_Tick(object sender, EventArgs e)
        {
            UpdateCPUTime();
        }
        private void UpdateCPUTime()
        {
            TimeSpan cpuTime = _currentProcess.TotalProcessorTime;
            cpuTimeLabel.Content = $"CPU 执行时间: {cpuTime}";
        }
        
        private async void StartServer()
        {
            try
            {
                _listener = new TcpListener(IPAddress.Any, Port);
                _listener.Start();
                AppendToConsole("等待客户端连接...");
                _ServerTcpClient = await _listener.AcceptTcpClientAsync();
                AppendToConsole("客户端已连接。");
                await ReceiveMessages();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private async Task ReceiveMessages()
        {
            try
            {
                var stream = _ServerTcpClient.GetStream();
                while (true)
                {
                    byte[] buffer = new byte[1024];
                    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    AppendToConsole($"接收到消息: {receivedMessage}");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"接收消息时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private async void StartClient()
        {
            try
            {
                _ClientTcpClient = new TcpClient();
                await _ClientTcpClient.ConnectAsync("127.0.0.1", Port);
                AppendToConsole("已连接到服务器。");
                await SendMessage("Hello from client!");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"连接或发送消息时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void AppendToConsole(string message)
        {
            Dispatcher.BeginInvoke(() =>
            {
                // 在界面上追加消息到文本框
                consoleTextBox.AppendText($"{message}\n");
                consoleTextBox.ScrollToEnd();
            });
        }

        private async Task SendMessage(string message)
        {
            try
            {
                await Task.Run(async () => {
                    while (true)
                    {

                        // 获取网络流
                        var stream = _ClientTcpClient.GetStream();

                        // 发送消息
                        byte[] buffer = Encoding.UTF8.GetBytes(message);
                        await stream.WriteAsync(buffer, 0, buffer.Length);

                        // 显示发送的消息
                        AppendToConsole($"发送消息: {message}");
                        Thread.Sleep(1000);
                    }
                });
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送消息时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}

执行结果
在这里插入图片描述

步骤1:启动资源监视器

  • 快速访问方法:按下Win + R组合键,输入resmon,然后按Enter键。
  • 或者,在任务管理器中,选择“性能”标签页,点击右下角的“资源监视器”。
    在这里插入图片描述
    选择WpfSample.exe程序
    在这里插入图片描述

步骤2:观察CPU使用情况

CPU选项卡
显示进程、服务、关联句柄和模块。帮助识别CPU使用率高峰和消耗大量CPU时间的进程。在“CPU”标签页,你可以看到所有正在运行的进程以及它们的CPU使用率。注意观察哪些进程在软件崩溃前后的使用率显著上升。
在这里插入图片描述

步骤3:检查内存使用

内存选项卡
显示物理和虚拟内存使用情况,提供有关已提交内存、待机内存和空闲内存的详细信息。切换到“内存”标签页,这里显示了每个进程的内存使用量。如果内存使用量接近或超过了可用内存,这可能是导致崩溃的原因之一。
在这里插入图片描述

步骤4:分析磁盘I/O

磁盘选项卡
提供磁盘活动、磁盘使用情况、存储卷和文件操作的信息。
“磁盘”标签页提供了磁盘活动的实时信息。检查是否有大量的磁盘读写操作正在进行,尤其是当你的软件涉及到数据库或文件操作时。
在这里插入图片描述

步骤5:监控网络流量

网络选项卡
监控网络活动、TCP连接、侦听端口和网络相关进程。
如果软件依赖于网络连接,那么“网络”标签页就非常重要。在这里,你可以看到哪些进程正在消耗网络带宽,以及它们的上传和下载速率。
在这里插入图片描述

4. 收集证据与分析

在客户机器上运行资源监视器时,要求客户在软件崩溃前后进行截图或录屏,以捕获关键数据。同时,使用性能监视器创建数据收集器集,以收集更长时间范围内的性能数据。

使用资源监视器诊断问题

  1. 分析CPU使用情况

    • 识别消耗高CPU资源的进程。
    • 确定程序执行期间是否有任何CPU峰值。
    • 检查是否有任何进程或服务可能干扰程序。
  2. 监控内存使用情况

    • 分析应用程序的内存消耗。
    • 检测可能导致性能下降的内存泄漏或过度内存使用。
    • 比较开发机器和客户机器之间的内存使用模式。
  3. 检查磁盘活动

    • 跟踪应用程序的磁盘I/O操作。
    • 识别由于磁盘读/写操作导致的瓶颈。
    • 比较不同环境中的磁盘使用情况,发现差异。
  4. 检查网络活动

    • 监控应用程序的网络连接和数据传输速率。
    • 识别可能影响应用程序性能的网络相关问题。
    • 验证应用程序是否正确与外部服务器或服务通信。

特别说明

在这里,我们可以看到,当前程序运行时,所依赖的动态库。这些动态库基本上可以划分为几类:

1、由我们的项目引用或者生成的。

在这里插入图片描述
程序运行过程中关联模块部分,由我们自己独立开发命名的模块。

2、dotnet运行环境的动态库。

在这里插入图片描述
这里可以知道,需要安装.NET Desktop Runtime 8.0.7和.NET Runtime 8.0.7。我们这里没有使用到ASP.NET Core Runtime 8.0.7,所以不需要安装这个。

3、VC++环境

在这里插入图片描述
从这里,可以知道,我们的程序需要安装VC++ runtime 14.40.33810.0版本的运行环境,

4、系统运行环境的动态库。

在这里插入图片描述
兼容windows sdk 10.0.2262.3733版本。

5. 其他库

在这里插入图片描述
可能会对程序的运行环境产生一定的影响。

5. 解决问题

根据收集到的信息,你可以开始分析问题。常见的原因包括但不限于:

  • 资源争用:其他高负载进程导致资源不足。
  • 驱动程序兼容性:特定的硬件驱动可能与软件不兼容。
  • 系统设置差异:不同的系统配置或安全设置可能导致问题。
  • 软件冲突:与其他正在运行的应用程序或服务发生冲突。

针对上述每一个可能的原因,都有相应的解决方案,比如优化代码、更新驱动、调整系统设置或修改防火墙规则。

6. 总结

当你面对程序在开发电脑上运行无误,但在客户机器上崩溃的情况时,不要惊慌。通过使用Windows资源监视器,你可以收集到宝贵的线索,帮助你像侦探一样追踪问题的根源。记住,每一次挑战都是成长的机会,让我们一起成为更好的问题解决者吧!

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值