C# 控制台应用程序在从 Visual studio 启动时确实有效,发布后它仅运行 3 个周期,这是正常行为吗?

C# Console application does work when started from Visual studio, after publishing it only runs for 3 cycles is this normal behaviour?

我制作了一个 C# 控制台应用程序 (.NET CORE 5.0) 以每分钟检查 MySQL 数据库中的更改并发送包含更改的电子邮件。

如果我 运行 这个应用程序直接来自 visual studio 2019,它工作正常没有任何问题。

如果我在发布后 运行 它,它只会执行 3 个周期并且控制台 window 保持打开状态。没有错误或其他任何错误。

第一张截图来自 运行ning via Visual Studio 2019

这是运行发布后直接从桌面截取的截图

        static void Main(string[] args)
        {

            
            TimerCallback callback = new TimerCallback(DoStuff);

            Timer stateTimer = new Timer(callback, null, 0, 1000);

            for (; ; )
            {
                Thread.Sleep(100);
            }

        }
        static public void DoStuff(Object stateInfo)
        {
            DataTable DtblEmployee = DatabaseClass.GetEmployeeList();

            foreach (DataRow row in DtblEmployee.Rows)
            {
                foreach (var item in row.ItemArray)
                {
                    string Str = RandomStringGenerator.GetRandomAlphanumericString(8);
                    DatabaseClass.EmployeeUpdate(item.ToString(), Str);
                    EmailClass.SendEmail(item.ToString(), Str);
                }
            }
            Console.WriteLine("Last check was @  {0}", DateTime.Now.ToString("h:mm:ss"));
            Console.WriteLine("{0} e-mail(s) were send.", DtblEmployee.Rows.Count);

        }

该程序应该 运行 永远。

很想听听你们的意见,如果需要更多信息,请询问。

编辑:添加了额外的代码来显示数据库


public static void EmployeeUpdate(string Emailadress, string Password)
        {
            string connectionstring;
            connectionstring = "server=1.1.1.1;user id=User;password=Pass;port=3306;persistsecurityinfo=True;database=Test";
            connection = new MySqlConnection(connectionstring);

            try
            {
                connection.Open();
                var cmd = new MySqlCommand("UPDATE Users SET Password=@param_val_1, GeneratePassword=@param_val_3 where Username=@param_val_2", connection);
                cmd.Parameters.AddWithValue("@param_val_1", Password);
                cmd.Parameters.AddWithValue("@param_val_2", Emailadress);
                cmd.Parameters.AddWithValue("@param_val_3", 0);

                cmd.ExecuteScalar();
                cmd.Dispose();
            }
            catch (MySqlException ex)
            {
                switch (ex.Number)
                {
                    case 0:
                        Console.WriteLine("Cannot connect to server.  Contact administrator");
                        break;

                    case 1045:
                        Console.WriteLine("Invalid username/password, please try again");
                        break;
                }
            }
            finally
            {
                connection.Close();
            }

        }

强烈 认为问题是您的 Timer 正在被垃圾收集和最终确定,这会阻止执行回调。

当您 运行 来自 Visual Studio 的代码在调试器 中时,JIT 对垃圾收集的积极性较低,这就是它在其中工作的原因情景.

要修复的最小更改是在 Main 方法的末尾添加此行:

GC.KeepAlive(stateTimer);

或者,根据 ,您可以对计时器使用 using 语句。任一选项都会产生在方法持续时间内使计时器保持活动状态的预期效果。

我认为您应该探索的另一种方法是根本不使用计时器,而只是在 Main 方法中循环并直接调用您的 DoStuff 方法。您仍然会在该循环中调用 Sleep ,这将处理计时方面的问题。显然,这会影响代码 运行 的精确计时,但最终可能会更易于理解和调试。

此外,我建议您更加谨慎地处理异常。确定是否希望代码在任何一次迭代抛出异常时停止循环,并在代码中明确说明。

我 100% 同意 JonSkeet 的观点。

是GC清理了stateTimer变量。在这一行之后,这个变量变为未使用,编译器可以自由地将它从堆栈中删除,然后 GC 可以自由删除计时器。

当您运行在不同的环境中运行您的应用程序时,GC 可能会使用不同的设置规则。调试会话、控制台应用程序、IIS 应用程序、SqlServer 模块等 - 它们对于 GC 的时间和强度都有不同的规则 运行。在调试会话下,它也可能会清理这个变量,但它也可能会在数小时或数天后清理,也许是为了给你更多时间来检查东西?在 free-running 控制台应用程序下,它发生得更快。

GC 也有必须始终遵守的硬性规定:如果使用了变量,则不能将其清除。

JonSkeet 建议固定 stateTimer,我不同意。这是一个 last-resort 选项。

更好,只需使用 USING 指令,因为 Timer 是一个 IDisposable:

        TimerCallback callback = new TimerCallback(DoStuff);

        using(Timer stateTimer = new Timer(callback, null, 0, 1000))
            for (; ; )
            {
                Thread.Sleep(100);
            }

这个变量还没有被使用,你甚至可以去掉它然后写

        using(new Timer(callback, null, 0, 1000))

但即使是现在,using() 语句仍会记住该 Timer 对象并防止 GC 过早清理它。 (它必须记住该对象才能在循环结束时调用 Dispose()..)