Multithreading 如何在windows服务中使用Threadpool.QueueUserWorkItem?
我有一个使用Threadpool.QueueUserWorkItem的windows服务。该服务连接到多个客户端数据库,获取数据,转换为XLS,并将文件发送到相应的FTP 关于下面的代码,我有3个问题:Multithreading 如何在windows服务中使用Threadpool.QueueUserWorkItem?,multithreading,c#-4.0,windows-services,queueuserworkitem,Multithreading,C# 4.0,Windows Services,Queueuserworkitem,我有一个使用Threadpool.QueueUserWorkItem的windows服务。该服务连接到多个客户端数据库,获取数据,转换为XLS,并将文件发送到相应的FTP 关于下面的代码,我有3个问题: private static System.Timers.Timer aTimer = new System.Timers.Timer(50000); public void OnStart(string[] args) { CLE.WriteToEve
private static System.Timers.Timer aTimer = new System.Timers.Timer(50000);
public void OnStart(string[] args)
{
CLE.WriteToEventLog("Service Started");
try
{
aTimer.Elapsed += new ElapsedEventHandler(PerformTimerOperation);
aTimer.Enabled = true;
}
catch (Exception ex)
{
CLE.WriteToEventLog("Error Starting Service: " + ex.Message);
}
}
private void PerformTimerOperation(object source, ElapsedEventArgs e)
{
CLE.WriteToEventLog("Timer Operation Started");
Clients objClient = new Clients();
List<Clients> objClientList = Clients.GetClientList();
foreach (var list in objClientList)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SendFilesToClient), list);
}
}
private void SendFilesToClient(Object stateInfo)
{
CLE.WriteToEventLog("Send Files To Client Started");
Clients oClient = (Clients)stateInfo;
CLE.WriteToEventLog("Start Proecessing Client: " + oClient.ClientName + ", ClientId: " + oClient.ClientId);
connectionString = App.Database.PrimaryConnectionString(oClient.ClientId);
string reports = oClient.Reports;
string[] values = reports.Split(',').Select(sValue => sValue.Trim()).ToArray();
foreach (string item in values)
{
//Send data to FTP based on cliend id
}
// At this point all reports are being sent to the FTP. We will update the database with LastExecutionDateTime + 1 hour. This will be used as DateFrom param for all reports for the next execution.
}
private static System.Timers.Timer aTimer = new System.Timers.Timer(50000);
public void OnStart(string[] args)
{
CLE.WriteToEventLog("Service Started");
try
{
aTimer.Elapsed += new ElapsedEventHandler(PerformTimerOperation);
aTimer.Enabled = true;
}
catch (Exception ex)
{
CLE.WriteToEventLog("Error Starting Service: " + ex.Message);
}
}
private void PerformTimerOperation(object source, ElapsedEventArgs e)
{
CLE.WriteToEventLog("Timer Operation Started");
Clients objClient = new Clients();
List<Clients> objClientList = Clients.GetClientList();
foreach (var list in objClientList)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(SendFilesToClient), list);
}
}
private void SendFilesToClient(Object stateInfo)
{
CLE.WriteToEventLog("Send Files To Client Started");
Clients oClient = (Clients)stateInfo;
CLE.WriteToEventLog("Start Proecessing Client: " + oClient.ClientName + ", ClientId: " + oClient.ClientId);
connectionString = App.Database.PrimaryConnectionString(oClient.ClientId);
string reports = oClient.Reports;
string[] values = reports.Split(',').Select(sValue => sValue.Trim()).ToArray();
foreach (string item in values)
{
//Send data to FTP based on cliend id
}
// At this point all reports are being sent to the FTP. We will update the database with LastExecutionDateTime + 1 hour. This will be used as DateFrom param for all reports for the next execution.
}
private static System.Timers.Timer aTimer=新系统.Timers.Timer(50000);
公共void OnStart(字符串[]args)
{
CLE.WriteToEventLog(“服务已启动”);
尝试
{
aTimer.Appead+=新的ElapsedEventHandler(执行时间属性);
aTimer.Enabled=true;
}
捕获(例外情况除外)
{
CLE.WriteToEventLog(“启动服务时出错:+ex.Message”);
}
}
私有void性能时间属性(对象源,ElapsedEventArgs e)
{
CLE.WriteToEventLog(“计时器操作已启动”);
Clients objClient=新客户端();
List objClientList=Clients.GetClientList();
foreach(ObjClient列表中的变量列表)
{
QueueUserWorkItem(新的WaitCallback(SendFileToClient),列表);
}
}
私有void SendFilesToClient(对象状态信息)
{
CLE.WriteToEventLog(“已启动向客户端发送文件”);
客户机oClient=(客户机)状态信息;
CLE.WriteToEventLog(“开始处理客户端:+oClient.ClientName+”,ClientId:+oClient.ClientId);
connectionString=App.Database.PrimaryConnectionString(oClient.ClientId);
string reports=oClient.reports;
string[]values=reports.Split(',')。选择(sValue=>sValue.Trim()).ToArray();
foreach(值中的字符串项)
{
//根据客户端id向FTP发送数据
}
//此时,所有报告都将发送到FTP。我们将使用LastExecutionDateTime+1小时更新数据库。这将用作下一次执行的所有报告的DateFrom参数。
}
该服务运行良好,我得到了适当的结果,但我需要确保我做得正确,不会在以后遇到问题。我假设您的服务将继续运行,而不是“一个完成”。如果是,请注意,
System.Timers.Timer
类的属性默认设置为true
。这仅仅意味着,每当50秒间隔过去时(50000毫秒=50秒),计时器将继续引发已用事件。如果您确信所有SendFilesToClient
操作都在下一个间隔时间过去之前的大量时间内完成,那么您应该可以。然而,我不会打赌。如果数据库在网络上,网络断开怎么办?如果服务在较慢的系统上运行,或者在内核较少的系统上运行,并且所有工作都没有及时完成,该怎么办
您可以通过如下方式关闭AutoReset
功能来解决此问题
private static var aTimer = new System.Timers.Timer(50000) { AutoReset = false };
这意味着已用事件将只触发一次。在performtimeropertion
中,只需将Enabled
属性重置为true
即可在退出前重新启动计时器
但这是一个不完整的解决方案,因为在计时器触发另一个已用事件之前,线程可能仍然需要很长时间才能完成。在这种情况下,您可能希望使用ManualResetEvent
在每个线程完成时发出信号,并暂停退出performtimeropertion
(并重置计时器),直到出现这种情况。比如说,
private void PerformTimerOperation(object source, ElapsedEventArgs e)
{
List<Clients> objClientList = new Clients().GetClientList();
List<ManualResetEvent> handles = new List<ManualResetEvent();
foreach (var list in objClientList)
{
// Create an MRE for each thread.
var handle = ManualResetEvent(false);
// Store it for use below.
handles.Add(handle);
// Notice two things:
// 1. Using new WaitCallback(...) syntax is not necessary.
// 2. Thread argument is now a Tuple object.
ThreadPool.QueueUserWorkItem(SendFilesToClient, Tuple.Create(list, handle));
}
// Wait for threads to finish.
WaitHandle.WaitAll(handles.ToArray());
// Reset the timer.
aTimer.Enabled = true;
}
以这种方式,performtimeropertion
将阻止调用,直到所有工作线程(例如,SendFilesToClient
)发出完成的信号。此时,重置计时器并在下一个间隔重复
对不起,时间太长了。希望能有帮助。刚刚在@Gray做了这件事。是C.NET 4.0我觉得这很好。关于你的问题#2,不应该有任何锁定的必要,因为你的线程完全独立运行。当您协调对共享资源的访问时,锁是必要的,但在这种情况下,每个工作线程都有自己的数据库连接,并且在一组不同的“东西”上运行。谢谢@GalacticCowboy!一点也不,马特。这是很好的解释。另外,仅供参考,此服务需要每1小时运行一次,以便将文件发送到FTP。因此,在完成上一个任务之前,我看不到服务开始运行有任何问题。这有什么区别吗?我是否仍应将您的解决方案(实现自动重置和手动重置)作为最佳实践?有关于锁定的信息吗?谢谢假设前一个任务仍在工作,新任务已启动。您正在打开数据库连接吗?您可以同时打开的连接数可能有限制。收件人呢?如果他们在第一个任务之前从第二个任务获取文件,这是问题吗?发生此类事件的可能性可能很小,但一个稳健的系统会处理此类问题。你比我更了解你的系统,所以用你的工程判断。如果performtimeropertion
通常不超过几分钟,我怀疑您没有问题。