Skip to main content
 首页 » 编程设计

c#之C# 中使用 while(true) 的内存问题

2024年08月12日32zhenyulu

我想用 C# 编写一个客户端,它检查用户是否登录到不同的客户端。客户端应 24/7 全天候运行并使用每个客户端的一些状态信息刷新数据库。

我的问题是:命令行工具占用越来越多的内存,所以我认为我分配的内存永远不会被释放是一个问题。

我认为我正在创建一个 ManagementScope,但我不能为它使用所有 Dispose() 方法。

这是我的代码:

static void Main(string[] args) 
    { 
 
        Ping pingSender = new Ping(); 
        PingOptions options = new PingOptions(); 
 
        string sqlconnectionstring = "Data Source=(local)\\SQLEXPRESS;Initial Catalog=clientstat;User ID=...;Password=....;Integrated Security=SSPI"; 
        SqlConnection clientread = new SqlConnection(sqlconnectionstring); 
        clientread.Open(); 
 
        // Use the default Ttl value which is 128, 
        // but change the fragmentation behavior. 
        options.DontFragment = true; 
 
        string username = ""; 
 
        // Create a buffer of 32 bytes of data to be transmitted. 
        string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 
        byte[] buffer = Encoding.ASCII.GetBytes(data); 
        int timeout = 120; 
 
        while (true) 
        { 
 
            SqlCommand clientcommand = new SqlCommand("SELECT * FROM Client WHERE StateID = @stateid", clientread); 
            clientcommand.Parameters.Add(new SqlParameter("stateid", 1)); 
 
            SqlDataReader clientreader = clientcommand.ExecuteReader(); 
            while (clientreader.Read()) 
            { 
                string ipadress = Convert.ToString(clientreader["IP"]); 
                string clientid = Convert.ToString(clientreader["ID"]); 
 
                if (ipadress != string.Empty && clientid != string.Empty) 
                { 
                    // First Try To Ping Computer 
 
                    PingReply reply = pingSender.Send(ipadress, timeout, buffer, options); 
                    if (reply.Status == IPStatus.Success) 
                    { 
 
                        try 
                        { 
 
                            ManagementScope managementScope = new ManagementScope((@"\\" + ipadress + @"\root\cimv2")); 
                            managementScope.Options.Username = "...."; 
                            managementScope.Options.Password = "..."; 
                            managementScope.Options.EnablePrivileges = true; 
                            // ObjectQuery to Check if User is logged on 
                            ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_ComputerSystem"); 
                            ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(managementScope, objectQuery); 
                            ManagementObjectCollection querycollection = managementObjectSearcher.Get(); 
 
                            foreach (ManagementObject mo in querycollection) 
                            { 
                                // Check Here UserName 
                                username = Convert.ToString(mo["UserName"]); 
                                if (username != "") 
                                { 
                                    Console.WriteLine(ipadress + " " + username); 
 
                                } 
 
                            } 
                            querycollection.Dispose(); 
                            managementObjectSearcher.Dispose(); 
                        } 
                        catch (Exception x) 
                        { 
                            Console.WriteLine(x.Message); 
                        } 
                    } 
                } 
                else 
                { 
                    Console.WriteLine(clientid + " has no IP-Adress in Database"); 
                } 
            } 
            clientcommand.Dispose(); 
            clientreader.Close(); 
            clientreader.Dispose(); 
        } 
 
    } 
 
} 

有什么我可以在这里改进的想法或建议吗?或者究竟是什么问题?

提前致谢

请您参考如下方法:

想法 1:

您必须处置 ManagementObject释放非托管 COM 资源。

不幸的是,there is a bug in the Dispose它的实现。 Here are more details关于它。

鸣谢应转到 this answer它提供了使用 GC.Collect() 的解决方法。不幸的是,它需要成本。

这就是为什么最好使用计数器每 n 次循环执行一次 GC.Collect,您将手动调整一个 n 值,直到性能可以接受为止。

无论如何,我会尝试使用反射调用 ManagementObject Dispose()。

想法 2:

一般来说,为多个查询重复使用一个打开的连接是不好的,因为它会阻止连接池机制发挥最佳作用。因此,如果使用的话,sqlconnection 可能会保留资源。

相反,请在循环中包含 SqlConnection create/open 和 close/dispose,与 this question 相关.