简体   繁体   English

2/4 System.Threading.Timers不在服务器上启动,但在本地运行良好

[英]2 of 4 System.Threading.Timers don't start on the server, but runs fine locally

I have a relatively simple console application. 我有一个相对简单的控制台应用程序。 In the Main -method I have the following code: Main方法中,我有以下代码:

TimeSpan immediately = TimeSpan.Zero;

var userRoutine = new System.Threading.Timer(
    o => SyncUsers(o), null, immediately, userMatchFrequency);
var materialRoutine = new Timer(
    o => SyncMaterials(o), null, immediately, materialSyncFrequency);
var activityRoutine = new Timer(
    o => SyncActivities(o), null, immediately, activitySyncFrequency);
var customerRoutine = new Timer(
    o => SyncCustomers(o), null, immediately, customerSyncFrequency);
while (true)
{
    Console.ReadKey(false);
}

And in the start of all the callback methods I have a Console.WriteLine(string) with a unique message on the very first line. 在所有回调方法的开头,我都有一个Console.WriteLine(string) ,在第一行上有一条唯一的消息。

I've then cleaned and rebuilt it in Visual Studio, then copied the bin->Release folder. 然后,我已经清理并在Visual Studio中对其进行了重建,然后复制了bin-> Release文件夹。 When I run the .exe file locally, it works fine. 当我在本地运行.exe文件时,它工作正常。 But when my boss runs it on the server, I can see that it only starts the last two Timers. 但是当老板在服务器上运行它时,我可以看到它仅启动最后两个计时器。 And it does so consistently. 而且它始终如一。

The project is targeted .NET 4.5.2 and that's the latest the server supports, so I doubt it's related to that. 该项目的目标是.NET 4.5.2,这是服务器支持的最新版本,因此我怀疑它是否与此有关。 All I've been able to find on it, is that I should keep a reference to all timers in the scope of the running thread, to avoid garbage collection. 我所能找到的就是,我应该在运行线程范围内保留对所有计时器的引用,以避免垃圾回收。 But to the best of my knowledge, the blocking Console.ReadKey() -call in a while loop doesn't make the outside scope "collectible" to the GC. 但是据我所知,在while循环中阻塞Console.ReadKey()调用不会使外部范围对GC来说是“可收集的”。

I don't have the luxury of testing a lot of hit and miss theories, so I'm probably going abandon Timers and use Task s that I manage manually - but I'm still curious what could be going on here. 我没有足够的能力来测试很多命中和失败的理论,所以我可能会放弃使用Timers并使用我手动管理的Task ,但是我仍然很好奇这里可能发生的事情。

GC is far more aggressive than you imagine. GC比您想象的要积极得多。 Eg an object can be collected whilst its constructor is still running, provided that the remaining part of the constructor doesn't access any of the object's fields. 例如,可以在对象的构造函数仍在运行时对其进行收集,前提是该构造函数的其余部分不访问该对象的任何字段。 Variable scope is not related to variable lifetime . 可变范围与可变寿命无关。

If you now are more suspicious of GC (and you should be), you can verify this supposition by adding some GC.KeepAlive s: 如果您现在对GC更加怀疑(应该如此),则可以通过添加一些GC.KeepAlive来验证这种假设:

TimeSpan immediately = TimeSpan.Zero;

var userRoutine = new System.Threading.Timer(
    o => SyncUsers(o), null, immediately, userMatchFrequency);
var materialRoutine = new Timer(
    o => SyncMaterials(o), null, immediately, materialSyncFrequency);
var activityRoutine = new Timer(
    o => SyncActivities(o), null, immediately, activitySyncFrequency);
var customerRoutine = new Timer(
    o => SyncCustomers(o), null, immediately, customerSyncFrequency);
while (true)
{
    Console.ReadKey(false);
}
GC.KeepAlive(userRoutine);
GC.KeepAlive(materialRoutine);
GC.KeepAlive(activityRoutine);
GC.KeepAlive(customerRoutine);

Though I'd generally look to restructure the application after this point so that the references are kept alive in a more organic fashion, having verified that it is a GC issue. 尽管我通常希望在此之后重新构建应用程序,以使引用以一种更加有机的方式保持活跃,并确认这是GC问题。

But to the best of my knowledge, the blocking Console.ReadKey()-call in a while loop doesn't make the outside scope "collectible" to the GC. 但据我所知,while循环中阻塞Console.ReadKey()的调用不会使外部范围对GC来说是“可收集的”。

That is not true. 那是不对的。 You don't use your local variables in any way, so nothing prevents GC from collecting them (though in Debug mode or when you run with debugger attached - measures might be taken to prevent that, but not in Release mode without debugger). 您不会以任何方式使用局部变量,因此不会阻止GC收集它们(尽管在Debug模式下或在连接调试器的情况下运行-可能会采取措施防止这种情况的发生,但如果没有调试器,则不会在Release模式下使用)。 It's easy to check with the following code: 使用以下代码很容易检查:

static void Main(string[] args) {
    TimeSpan immediately = TimeSpan.Zero;
    var userMatchFrequency = TimeSpan.FromSeconds(1);
    var materialSyncFrequency = TimeSpan.FromSeconds(2);
    var activitySyncFrequency = TimeSpan.FromSeconds(3);
    var customerSyncFrequency = TimeSpan.FromSeconds(4);
    var userRoutine = new System.Threading.Timer(
        o => SyncUsers(o), null, immediately, userMatchFrequency);
    var materialRoutine = new Timer(
        o => SyncMaterials(o), null, immediately, materialSyncFrequency);
    var activityRoutine = new Timer(
        o => SyncActivities(o), null, immediately, activitySyncFrequency);
    var customerRoutine = new Timer(
        o => SyncCustomers(o), null, immediately, customerSyncFrequency);

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();
    Console.ReadKey(false);
}

static void SyncUsers(object o) {
    Console.WriteLine("Sync users");
}

static void SyncMaterials(object o)
{
    Console.WriteLine("Sync meterials");
}

static void SyncActivities(object o)
{
    Console.WriteLine("Sync activities");
}

static void SyncCustomers(object o)
{
    Console.WriteLine("Sync customers");
}

If you compile this in Release mode and run without debugger - you won't see any messages in console at all, because all your timers will be immediately garbage collected. 如果您在发布模式下进行编译并在没有调试器的情况下运行-您将根本不会在控制台中看到任何消息,因为所有计时器都会立即被垃圾回收。 To solve this problem - use GC.KeepAlive : 要解决此问题-使用GC.KeepAlive

static void Main(string[] args) {
    TimeSpan immediately = TimeSpan.Zero;
    var userMatchFrequency = TimeSpan.FromSeconds(1);
    var materialSyncFrequency = TimeSpan.FromSeconds(2);
    var activitySyncFrequency = TimeSpan.FromSeconds(3);
    var customerSyncFrequency = TimeSpan.FromSeconds(4);
    var userRoutine = new System.Threading.Timer(
        o => SyncUsers(o), null, immediately, userMatchFrequency);
    var materialRoutine = new Timer(
        o => SyncMaterials(o), null, immediately, materialSyncFrequency);
    var activityRoutine = new Timer(
        o => SyncActivities(o), null, immediately, activitySyncFrequency);
    var customerRoutine = new Timer(
        o => SyncCustomers(o), null, immediately, customerSyncFrequency);

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
    GC.WaitForPendingFinalizers();
    Console.ReadKey(false);

    GC.KeepAlive(userRoutine);
    GC.KeepAlive(materialRoutine);
    GC.KeepAlive(activityRoutine);
    GC.KeepAlive(customerRoutine);
}

Or better - dispose your timers after Console.ReadKey (or wrap in using): 或更好-在Console.ReadKey之后放置计时器(或使用包装):

static void Main(string[] args) {
    TimeSpan immediately = TimeSpan.Zero;
    var userMatchFrequency = TimeSpan.FromSeconds(1);
    var materialSyncFrequency = TimeSpan.FromSeconds(2);
    var activitySyncFrequency = TimeSpan.FromSeconds(3);
    var customerSyncFrequency = TimeSpan.FromSeconds(4);
    using (new Timer(o => SyncUsers(o), null, immediately, userMatchFrequency))
    using (new Timer(o => SyncMaterials(o), null, immediately, materialSyncFrequency))
    using (new Timer(o => SyncActivities(o), null, immediately, activitySyncFrequency))
    using (new Timer(o => SyncCustomers(o), null, immediately, customerSyncFrequency))
         Console.ReadKey(false);                    
}

Timers implement IDisposable and it's a good practice to dispose everything that implements it anyway. 计时器实现IDisposable ,因此最好处置掉所有实现它的东西。 This will also prevent their garbage collection. 这也将阻止其垃圾回收。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Windows服务中System.Threading.Timers的准确性 - Accuracy of the System.Threading.Timers in windows service 回调为静态时,EF数据库上下文会阻止所有System.Threading.Timers - EF Database context blocks all System.Threading.Timers when callback is static 在 System.Threading.Timers TimerCallback 事件处理程序中捕获异常,然后重新抛出未发送回主线程 - Exception caught in System.Threading.Timers TimerCallback event handler and then re-thrown not sent back to main thread GitLab:未找到项目:在本地运行良好,无法正常部署 - GitLab: Project Not Found: Runs Fine Locally, Doesn't Work Deployed 使用System.Threading与System.Timers编写计时器 - Writing a timer with System.Threading vs System.Timers System.Timers.Timer和System.Threading.Timer的不同行为 - Different behaviour of System.Timers.Timer and System.Threading.Timer 使用 System.Threading 在 C# 中同时运行多个计时器 - Running multiple timers simultaneously in C# using System.Threading 网站在本地运行良好,但在Windows Azure上崩溃 - Website runs fine locally but crashes on Windows Azure 识别不同的计时器运行(System.Timers.Timer) - Identify different timer runs (System.Timers.Timer) 在应用程序启动时出现EntryPointNotFoundException TaskDialog,但稍后运行良好 - EntryPointNotFoundException TaskDialog at start of Application but runs fine later
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM