![](/img/trans.png)
[英]Visual Studio 2008 - Issues with publishing C# Console Application
[英]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 个周期并且控制台窗口保持打开状态。 没有错误或其他任何东西。
第一个屏幕截图来自通过 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);
或者,根据quetzalcoatl 的回答,您可以对计时器使用using
语句。 任一选项都将具有在该方法的持续时间内保持计时器处于活动状态的预期效果。
我认为您应该探索的另一种方法是根本不使用计时器,而只是在Main
方法中循环并直接调用您的DoStuff
方法。 您仍然会在该循环中调用Sleep
,它将处理计时方面。 显然,这会影响代码运行的精确时间,但最终可能会更易于理解和调试。
此外,我建议您对异常处理更加有意。 确定您是否希望代码在任何一次迭代抛出异常时停止循环,并在代码中明确说明。
我 100% 同意 JonSkeet 的原因。
它是清理 stateTimer 变量的 GC。 在这一行之后,这个变量变为未使用状态,编译器可以自由地从堆栈中清除它,然后 GC 可以自由地清除计时器。
当您在不同环境中运行应用程序时,GC 可能使用不同的设置规则。 调试会话、控制台应用程序、IIS 应用程序、SqlServer 模块等 - 它们在何时以及如何积极运行 GC 方面都有不同的规则。 在调试会话下,它也可以清理这个变量,但它也可以在几小时或几天后做,也许是为了给你更多的时间来检查事情? 在自由运行的控制台应用程序下,它发生得更快。
GC 也有必须始终遵守的硬性规则:如果使用变量,则不能清除它。
JonSkeet 建议固定 stateTimer,我不同意。 这是最后的选择。
更好的是,只需使用 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() ..)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.