2

Threadpool.QueueUserWorkItemを使用しているWindowsサービスがあります。このサービスは、複数のクライアントデータベースに接続し、データを取得し、XLSに変換して、対応するFTPにファイルを送信します。

以下のコードに関して3つの質問があります。

  1. Threadpool.QueueUserWorkItemを正しく使用していますか?
  2. 問題を回避するために、コードのどこかでロックを使用する必要がありますか?はいの場合、どこでどのオブジェクトに。
  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.
        }

サービスは正常に機能し、適切な結果が得られますが、正しく実行していることを確認し、後で問題が発生しないようにする必要があります。

4

1 に答える 1

5

私はあなたのサービスが「1回限り」ではなく実行し続けることを意図していると思います。その場合、クラスのAutoResetプロパティはデフォルトでに設定されていることに注意してください。これは単に、50秒の間隔が経過するたびにタイマーがイベントを発生させ続けることを意味します(50000ミリ秒= 50秒)。次の間隔が経過する前にすべての 操作が十分な時間で完了することが確実にわかっている場合は、問題ないはずです。しかし、私はそれには賭けません。データベースがネットワーク上にあり、ネットワークがダウンした場合はどうなりますか?サービスが低速のシステムまたはコアの少ないシステムで実行されていて、すべての作業が時間内に完了していない場合はどうなりますか?System.Timers.TimertrueElapsedSendFilesToClient

AutoResetこのように機能をオフにすることで、これを回避できる可能性があります。

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

これは、Elapsedイベントが1回だけ発生することを意味します。内で、プロパティをPerformTimerOperationリセットして、終了する前にタイマーを再起動します。Enabledtrue

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

申し訳ありませんが、これはとても長いです。それが役に立てば幸い。

于 2013-02-26T19:25:57.450 に答える