如何在windows服务中使用Threadpool.QueueUserWorkItem



我有一个windows服务,我正在使用Threadpool.QueueUserWorkItem。该服务连接到多个客户端数据库,获取数据,转换为XLS,并将文件发送到相应的FTP。

关于下面的代码,我有3个问题:

  1. 我是否正确使用了Threadpool.QueueUserWorkItem
  2. 我是否需要在代码中的任何位置使用Lock来避免问题?如果是,在哪里以及指向什么对象
  3. 代码中有什么不正确的地方吗?如果是,如何处理

代码:

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.
        }

这项服务运行良好,我得到了适当的结果,但我需要确保我做得正确,以后不会遇到问题。

我假设您的服务是为了保持运行,而不是"一次性完成"。如果是,请注意System.Timers.Timer类的AutoReset属性默认设置为true。这只是意味着,每当经过50秒的间隔(50000毫秒=50秒(时,计时器将继续引发Elapsed事件。如果您确信所有SendFilesToClient操作在下一个间隔之前都会在足够的时间内完成,那么您应该没事。但是,我不会打赌。如果数据库在网络上,而网络出现故障怎么办?如果服务运行在速度较慢的系统或内核较少的系统上,并且所有工作都没有及时完成,该怎么办?

你可以像这样关闭AutoReset功能来解决这个问题

private static var aTimer = new System.Timers.Timer(50000) { AutoReset = false };

这意味着Elapsed事件将只触发一次。在PerformTimerOperation中,只需将Enabled属性重置为true即可在退出前重新启动计时器。

但这是一个不完整的解决方案,因为在计时器触发另一个Elapsed事件之前,线程可能仍然需要很长时间才能完成。在这种情况下,您可能希望使用ManualResetEvent在每个线程完成时发出信号,并挂起退出PerformTimerOperation(并重置计时器(,直到出现这种情况。例如,

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;
}

现在更新SendFilesToClient

private void SendFilesToClient(Object stateInfo)
{
    // The parameter is now a Tuple<T1, T2>, not a Clients object.
    var tuple = (Tuple<Clients, ManualResetEvent>)stateInfo;
    try
    {
        Clients oClient = tuple.Item1;
        // Do your work here...
    }
    catch (Exception ex)
    {
        // Handle any exception here.
    }
    finally
    {
        // Signal that the work is done...even if an exception occurred.
        // Otherwise, PerformTimerOperation() will block forever.
        ManualResetEvent mreEvent = tuple.Item2;
        mreEvent.Set();
    }
}

以这种方式,PerformTimerOperation将阻塞WaitHandle.WaitAll()调用,直到所有工作线程(例如SendFilesToClient(发出它们完成的信号。此时,您将重置计时器,并在下一个间隔中重复。

对不起,时间太长了。希望能有所帮助。

相关内容

  • 没有找到相关文章

最新更新