У меня есть служба Windows, которая работает каждые 4 минуты. Когда таймер показывает, запускает ли DataImporter, DataImporter имеет несколько «заданий», которые он может выполнять на каждом тике, например, есть задание ProcessData и задание RetreiveData:
- Задание RetreiveDataбудет обращаться к стороннему API и сохранять данные в БД для обработки.
- Задание ProcessData будет извлекать данные из БД и обрабатывать их в нашей полезной БД и т. д.
Как толькоDataImporter запускается, он проверяет таблицу БД с именем ScheduledJob - она имеет ряд функций планирования, таких как FrequencyInterval, время ActiveStart / Stop, время StartedLastRun. Таблица ScheduledJob имеет флаг «InProgress», этот флаг останавливает DataImport, который подхватывает это задание, когда оно уже выполняется.
Существует постоянная проблема, когда задание выбирается дважды, на расстоянии нескольких секунд друг от друга, а затем запускается одновременно, что приводит к ряду ограничений БД при попытке вставить идентичные записи. Я не совсем уверен, как он может поднять две работы одновременно, отметка времени составляет 4 минуты, поэтому теоретически он не должен иметь возможности даже смотреть на потенциальные задания, которые могут выполняться, как он может выполнять их обес интервалом в несколько секунд?
Необходимо, чтобы задания RetrieveData и ProcessData выполнялись параллельно, поэтому я не могу приостанавливать таймер во время выполнения задания.
Служба:
public partial class DataImport : ServiceBase
{
private int _eventId = 0;
readonly Timer _serviceTimer = new Timer(240000);
public DataImport()
{
InitializeComponent();
ImportServiceEventLog.Source = ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource(); ;
}
protected override void OnStart(string[] args)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " started", EventLogEntryType.Information, _eventId++);
_serviceTimer.AutoReset = true;
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " timer interval = " + _serviceTimer.Interval / 1000 + " seconds", EventLogEntryType.Information, _eventId++);
_serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer);
_serviceTimer.Start();
}
protected override void OnStop()
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " stopped", EventLogEntryType.Information, _eventId++);
}
public void OnTimer(object sender, ElapsedEventArgs args)
{
try
{
Run();
}
catch (System.Exception ex)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " error: " + ex.ToString(), EventLogEntryType.Information, _eventId++);
}
}
public void Run()
{
using (var dataImportController = new DataImportController())
{
dataImportController.Run();
}
}
}
DataImportController:
public class DataImportController
{
public void Run()
{
// Gets all the jobs from the ScheduledJob table in the DB
var jobs = GetJobsToRun();
//Get all Processes (from DB)
foreach (var job in jobs)
{
//Check the time it was last run - do this for each process
if (RunJob(job))
{
_messaging.EventMessage("Process " + job.Name + " finished : " + DateTime.Now, ServiceSource.DATA_IMPORT_SERVICE);
}
}
}
public bool RunJob(ScheduledJob job)
{
// Checks if the job is ready to run, i.e. is the InProgress flag set to false and the interval long enough since the StartedLastRun DateTime
if (!job.IsReadyToRun())
{
return false;
}
// Set job to in progress
job.InProgress = true;
job.StartedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
try
{
switch (job.Name.ToUpper())
{
case "RetreiveData":
// RUN JOB
break;
case "ProcessData":
// RUN JOB
break;
}
job.InProgress = false;
job.EndedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
}
catch (Exception exception)
{
_messaging.ReportError("Error occured whilst checking we are ready to run " + exception.Message, exception, null, 0, ServiceSource.DATA_IMPORT_SERVICE);
}
return true;
}
}
РЕДАКТИРОВАТЬ:
Включить Program.cs
static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataImport()
};
ServiceBase.Run(ServicesToRun);
}
}