这是我第一次尝试使用计时器,特别是在这里使用System.Threading.Timer,很明显我错过了对这个概念的一些理解。我想解决的任务如下:
我们每天从外部源接收多个数据集,我们将其编译成报告并在内部分发。外部数据在某个时间安排,但此交付有时会延迟。我们希望每天尽快分发我们的报告,因此我们尝试与预定的交付时间密切相关地阅读和编译报告。但是,在某些或所有数据被延迟的日子里,我们希望继续查找它,直到收到它为止,或者如果长时间通过,则报告丢失的数据。
下面是一个非常简化的示例代码,旨在实现此行为。但是,当我运行此代码时,代码将以每个报告的一次调用计时器回调方法的方式进行操作并结束。
我希望这段代码能做什么:main方法应该创建ReportTimer的两个实例,每个reportItem一个。这些ReportTimers应该有自己的两个定时器,它们应该调用GenerateReport方法并尝试编译报告。如果没有可用的数据,计时器应重新安排自己并在第二次尝试后5秒内再次尝试(或者更确切地说,当它第三次进入方法时),它应该退出重试并结束。如果我们使用count参数调用ReportTimer为4或更高,它最终应该在控制台上打印报告消息然后退出。
实际发生的情况:实例化两个ReportTimers并调用第一次调用GenerateReport,我们在控制台上收到报告尚未准备好的消息。然后执行结束,不再向控制台写入。
我的问题有两个:第一个问题:为什么会出现这种情况?其次,是否有另一种更优选的方式来完成我想做的事情?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Reporter
{
class Program
{
static void Main()
{
List<string> reportItems = new List<string>() { "reportItem1", "reportItem2" };
foreach (string reportItem in reportItems)
{
ReportTimer rt = new ReportTimer(reportItem, 2);
Thread.Sleep(1000);
}
}
}
public class ReportTimer
{
int _counter, _maxCount;
int _timerIntervalInMilliSeconds = 5 * 1000;
string _reportItem;
Timer _timer;
bool _noMoreRuns = false;
DateTime _reportDateReadyAt = DateTime.Now.AddSeconds(12); // For the purpose of this example let's assume the report data is available in 12 seconds from start
public ReportTimer(string item, int countTo)
{
_counter = 0;
_maxCount = countTo;
_reportItem = item;
_timer = new Timer(GenerateReport, null, 0, Timeout.Infinite); //Immediately start an attempt to generate a report.
}
public void GenerateReport(object state)
{
_counter++;
if (ReadyToReport(_reportItem))
{
Console.WriteLine("This is the report of {0}.", _reportItem);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
_noMoreRuns = true;
}
else if (_counter <= _maxCount)
Console.WriteLine("Report of {0} is not ready yet - {1}", _reportItem, DateTime.Now.ToLongTimeString());
else
{
Console.WriteLine("The report of {0} could not be generated", _reportItem);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
_noMoreRuns = true;
}
if (_noMoreRuns)
_timer.Dispose();
else
_timer.Change(_timerIntervalInMilliSeconds, Timeout.Infinite);
return;
}
private bool ReadyToReport(string _reportItem)
{
bool isReady = false;
if (DateTime.Now >= _reportDateReadyAt)
isReady = true;
return isReady;
}
}
}