我构建了一个服务,该服务将调用要执行的任务。这是表驱动的和动态的。因此,在服务的每个滴答声(每10秒钟一次)中,它将调用SQL表并找出活动状态。如果处于活动状态,它将构建一个新任务并将其放入对象列表。每个任务调用相同的函数,但是我将唯一的数据行(其中包含数据表中的唯一ID)传递给方法的传递参数。
当任务同时运行时,我从数据库调用中收到与应该运行的任务无关的随机错误。
所以问题是,同时运行相同方法/功能的任务是否可能会彼此步进并导致线程相交?
这是我的代码:
namespace ReportService
{
public partial class Service1 : ServiceBase
{
//public timer
private static System.Timers.Timer timerReports = null;
//collection of tasks
//private static BlockingCollection<ReportTasks> tasksCollection = new BlockingCollection<ReportTasks>();
private static List<ReportTasks> tasksCollection = new List<ReportTasks>();
#region Service functions
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
try
{
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "L";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.OptionalMessage = "Service On";
Log.WriteToLog(exceptioninfo, ConfigSettings.GetServiceSettings());
timerReports = new System.Timers.Timer();
timerReports.Interval = 30000; //30 secconds
timerReports.Elapsed += new ElapsedEventHandler(this.timerReports_Tick);
timerReports.Enabled = true;
}
catch (Exception ex)
{
//Get Service settings
dynamic ServiceSettings = ConfigSettings.GetServiceSettings();
if (ex.Data["WrittenToLog"] == null)
{
//set new object
ex.Data.Add("WrittenToLog", true);
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
else if ((bool)ex.Data["WrittenToLog"] == false)
{
//set written to log as true
ex.Data["WrittenToLog"] = true;
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
//throw exception to back out of process
//throw ex;
}
}
protected override void OnStop()
{
try
{
timerReports.Enabled = false;
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "L";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.OptionalMessage = "Service Off";
Log.WriteToLog(exceptioninfo, ConfigSettings.GetServiceSettings());
}
catch (Exception ex)
{
//Get Service settings
dynamic ServiceSettings = ConfigSettings.GetServiceSettings();
if (ex.Data["WrittenToLog"] == null)
{
//set new object
ex.Data.Add("WrittenToLog", true);
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
else if ((bool)ex.Data["WrittenToLog"] == false)
{
//set written to log as true
ex.Data["WrittenToLog"] = true;
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
//throw exception to back out of process
//throw ex;
}
}
#endregion
private void timerReports_Tick(object sender, ElapsedEventArgs e)
{
//check to see if task is running, if not, process EDI
try
{
//remove completed tasks
tasksCollection.RemoveAll(item => item.ReportTask.Status == TaskStatus.RanToCompletion);
//Get Service settings
dynamic ServiceSettings = ConfigSettings.GetServiceSettings();
//set the SQL command and parameters
SQLcommand Sqlcommandobj = new SQLcommand();
Sqlcommandobj.SQLcmd = @"SELECT *,'TABLE' AS [TABLE_NAME]
FROM EX_TABLE";
Sqlcommandobj.SQLcmdType = CommandType.Text;
//fill in list
DataSet dsReportSchedules = Queries.ServiceSQLExecute(ServiceSettings, Sqlcommandobj);
//loop through each schedule to add/remove tasks
foreach (DataRow drReport in dsReportSchedules.Tables["SCHEDULES"].Rows)
{
if (!tasksCollection.Any(item => item.ReportID == Helper.GetValueFromDataRowInt32(drReport, "REPORTS_SCHEDULE_ID")))
{
if (Helper.GetValueFromDataRowString(drReport, "ACTIVE") == "1" && Helper.GetValueFromDataRowString(drReport, "DELETE_DATE") == string.Empty)
{
//create cancellation for task
var ts = new CancellationTokenSource();
//create new task
Task newTask = new Task(() => ReportProcess.BeginProcessingReport(drReport, ServiceSettings), ts.Token);
//fill in report tasks object
ReportTasks ReportTasks = new ReportTasks();
ReportTasks.ReportID = Helper.GetValueFromDataRowInt32(drReport, "REPORTS_SCHEDULE_ID");
ReportTasks.ReportName = Helper.GetValueFromDataRowString(drReport, "NAME");
ReportTasks.ReportTask = newTask;
ReportTasks.TaskID = newTask.Id;
ReportTasks.Active = Convert.ToBoolean(drReport["ACTIVE"]);
ReportTasks.CancelTokenSource = ts;
//add to task collection
tasksCollection.Add(ReportTasks);
}
}
else
{
//remove if not active or deleted
if (Helper.GetValueFromDataRowString(drReport, "ACTIVE") != "1" || Helper.GetValueFromDataRowString(drReport, "DELETE_DATE") != string.Empty)
{
var itemToRemove = tasksCollection.SingleOrDefault(item => item.ReportID == Helper.GetValueFromDataRowInt32(drReport, "REPORTS_SCHEDULE_ID"));
if (itemToRemove.ReportID > 0)
{
//check to see if task is running
if (itemToRemove.ReportTask.Status == TaskStatus.Running)
{
itemToRemove.CancelTokenSource.Cancel();
}
//remove task from collection
tasksCollection.Remove(itemToRemove);
}
}
}
}
//trigger each task
foreach (var str in tasksCollection)
{
Console.WriteLine("Task: " + str.ReportName + " - Status: " + str.ReportTask.Status);
if (str.ReportTask.Status == TaskStatus.RanToCompletion | str.ReportTask.Status == TaskStatus.Created)
{
str.ReportTask.Start();
}
}
}
catch (Exception ex)
{
//Get Service settings
dynamic ServiceSettings = ConfigSettings.GetServiceSettings();
if (ex.Data["WrittenToLog"] == null)
{
//set new object
ex.Data.Add("WrittenToLog", true);
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
else if ((bool)ex.Data["WrittenToLog"] == false)
{
//set written to log as true
ex.Data["WrittenToLog"] = true;
//build exception object
ExceptionInfo exceptioninfo = new ExceptionInfo();
exceptioninfo.LogType = "E";
exceptioninfo.ClassName = MethodBase.GetCurrentMethod().DeclaringType.Name.ToString();
exceptioninfo.MethodName = MethodBase.GetCurrentMethod().Name;
exceptioninfo.InnerException = ex;
//write to log
Log.WriteToLog(exceptioninfo, ServiceSettings);
}
//throw exception to back out of process
//throw ex;
}
}
}
}
在上面的计时器滴答中,我浏览了任务列表中的每个记录,并删除了已完成的任务。然后,我从数据库中获取记录并确定是否需要添加它们。它还将检查用户是否将其标记为已删除或无效,并使用取消令牌将其停止,然后从列表中将其删除。最后,它将遍历并触发列表中的任务。
这是在每个任务上调用的过程:
public static void BeginProcessingReport(Object drReportSchedule, dynamic
ServiceSettings)
{
}
,这里是包含List <>
任务的对象public struct ReportTasks
{
public string ReportName;
public Int32 ReportID;
public int TaskID;
public Task ReportTask;
public bool Active;
public CancellationTokenSource CancelTokenSource;
public ReportTasks(string name, Int32 reportID, int id, Task task, DataRow drReport, bool active, CancellationTokenSource canceltokensource, CancellationToken canceltoken)
{
ReportName = name;
ReportID = reportID;
TaskID = id;
ReportTask = task;
Active = active;
CancelTokenSource = canceltokensource;
}
}
希望这是足够的信息,我们将不胜感激
更新:我能够通过为任务的主要处理创建线程来解决问题。将功能移出tick方法,然后将其放在自己的方法中,以供线程调用。
因此,计时器中的每个滴答声都会检查线程是否在运行。
private static Thread mainThread = null;
private void timerReports_Tick(object sender, ElapsedEventArgs e)
{
//create thread if null
if (mainThread == null)
{
mainThread = new Thread(new ThreadStart(Process));
}
//start thread if stopped or unstarted, else, it is still running and do nothing
if(mainThread.ThreadState == System.Threading.ThreadState.Stopped || mainThread.ThreadState == System.Threading.ThreadState.Unstarted)
{
mainThread.Start();
}
}
更新:
上面的代码没有用,因为当任务没有完成时,我仍然遇到重新触发任务的问题。
所以让我改写我的问题: 如何创建动态任务/线程以触发与从数据库控制任务/线程相同的函数/方法?如果删除记录或将记录设置为Active = 0(这是SQL中的位字段,以确定记录是否应该触发),则将需要具有删除线程和任务的功能。
答案 0 :(得分:-2)
“互相踩踏”是指滴答事件再次发生而前一个事件仍在处理中吗?
在这种情况下,只需在滴答事件开始时禁用计时器,并在处理完成后重新启用