[英]2 of 4 System.Threading.Timers don't start on the server, but runs fine locally
我有一個相對簡單的控制台應用程序。 在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);
}
在所有回調方法的開頭,我都有一個Console.WriteLine(string)
,在第一行上有一條唯一的消息。
然后,我已經清理並在Visual Studio中對其進行了重建,然后復制了bin-> Release文件夾。 當我在本地運行.exe文件時,它工作正常。 但是當老板在服務器上運行它時,我可以看到它僅啟動最后兩個計時器。 而且它始終如一。
該項目的目標是.NET 4.5.2,這是服務器支持的最新版本,因此我懷疑它是否與此有關。 我所能找到的就是,我應該在運行線程范圍內保留對所有計時器的引用,以避免垃圾回收。 但是據我所知,在while循環中阻塞Console.ReadKey()
調用不會使外部范圍對GC來說是“可收集的”。
我沒有足夠的能力來測試很多命中和失敗的理論,所以我可能會放棄使用Timers並使用我手動管理的Task
,但是我仍然很好奇這里可能發生的事情。
GC比您想象的要積極得多。 例如,可以在對象的構造函數仍在運行時對其進行收集,前提是該構造函數的其余部分不訪問該對象的任何字段。 可變范圍與可變壽命無關。
如果您現在對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);
盡管我通常希望在此之后重新構建應用程序,以使引用以一種更加有機的方式保持活躍,並確認這是GC問題。
但據我所知,while循環中阻塞Console.ReadKey()的調用不會使外部范圍對GC來說是“可收集的”。
那是不對的。 您不會以任何方式使用局部變量,因此不會阻止GC收集它們(盡管在Debug模式下或在連接調試器的情況下運行-可能會采取措施防止這種情況的發生,但如果沒有調試器,則不會在Release模式下使用)。 使用以下代碼很容易檢查:
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");
}
如果您在發布模式下進行編譯並在沒有調試器的情況下運行-您將根本不會在控制台中看到任何消息,因為所有計時器都會立即被垃圾回收。 要解決此問題-使用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);
}
或更好-在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);
}
計時器實現IDisposable
,因此最好處置掉所有實現它的東西。 這也將阻止其垃圾回收。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.