2016-09-21 33 views
0

処理中に例外が発生するまで、Windowsサービスは正常に動作しています。 これには2つのGenerateInvoiceとGenerateReportが含まれています。 これらのスレッドはブロックされており、主にDataBaseサーバーでCPU使用率が高い場合にDeadLockのような状況になります。Windowsサービスは実行中ですがコードは実行されていません

コードの下に条件が追加されているような状況を処理するためにコードを変更しましたが、それでも機能しません。以下 は、サービスのOnStart()方法であって、ここで

protected override void OnStart(string[] args) 
{ 
    try 
    { 
     log.Debug("Starting Invoice Generation Service"); 
     _thread = new Thread(new ThreadStart((new GenerateInvoice()).Process)); 
     _thread.IsBackground = true; 
     _thread.Start(); 

     _reportThread = new Thread(new ThreadStart((new GenerateReport()).Process)); 
     _reportThread.IsBackground = true; 
     _reportThread.Start(); 
    } 
    catch (Exception ex) 
    { 
     log.Error("Error in Invoice Generation Service:", ex); 
    } 
} 

は、最初のスレッドの処理コードです:セカンドスレッドすなわちGenerateReportコードのGenerateInvoiceは

public void Process() 
{ 
    while (isProcessActive) 
    { 
     try 
     { 
      DBBilling obj = new DBBilling(); 
      DataTable dtInvoiceID = obj.readData(@"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail, 
       i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number 
       FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID 
       JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID 
       Where i.[STATUS] = 'PENDING') AS rows 
       WHERE ROW_NUMBER=1 ORDER BY UPDATETIME"); 

      processCounter = 0; 

      #region process 
      if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0) 
      { 
       //some code here.. 
      } 
      #endregion 
     } 
     catch (Exception ex)  //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016 
     { 
      log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex); 
      if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)) 
      { 
       processCounter++; 
       if (processCounter >= 1) //Need to change to 25 after Problem Solve 
       { 
        isProcessActive = false; 
        log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back     
       } 
       else 
        System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec 
      } 
     }     
    }   
} 

処理:

public void Process() 
{ 
    AppSettingsReader ar = new AppSettingsReader(); 
    string constr = (string)ar.GetValue("BillingDB", typeof(string)); 
    SqlConnection con = new SqlConnection(constr); 
    while (isProcessActive) 
    { 
     try 
     { 
      DBBilling obj = new DBBilling(); 
      DataTable dtReportRunID = obj.readData(@"SELECT ReportRunID,MonYear, BeginDate, EndDate FROM ReportRunRequest 
       Where [STATUS] = 'PENDING' ORDER BY ReportRunID"); 
      processCounter = 0; 

      if (dtReportRunID != null && dtReportRunID.Rows.Count > 0) 
      { 
       //some code here.. 
      } 
     } 
     catch (Exception ex)  //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016 
     { 
      log.ErrorFormat("Generate Report -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex); 
      if (DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)) 
      { 
       processCounter++; 
       if (processCounter >= 1) //Need to change to 25 after Problem Solve 
       { 
        isProcessActive = false; 
        log.ErrorFormat("Generate Report -> Process -> RunInvoice Service Exiting loop"); //From here control is not going back        
       } 
       else 
        System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec 
      } 
     } 
    } 
} 

可能何そのような状態を避けるための解決策?

+0

のみバックグラウンドスレッドを使用する場合に問題が発生します。私はサービスが役に立って何かを得るのに十分長く実行されて驚いています。前景スレッドがまったくないので、起動直後にシャットダウンする必要があります。 –

+1

ループ中の「Timer」を使わないのはなぜですか?それは良い習慣ではなく、あなたにいくつかの誤りをもたらす可能性があります。また、 'Thread.Sleep()'を使うことも良いことではありません。スリープは、デバッグの目的でのみ使用してください。 –

+1

@Damien_The_Unbeliever、サービスでバックグラウンドスレッドを使用するのに問題はありません。サービスは停止するまで機能します。 Windowsサービスのバックグラウンドスレッドで使用すると良いです。その場合、サービスをeasely停止することができます。 –

答えて

0

私が示唆しているのは、無限ループの代わりにTimerを使用することです。前述のように、他の応答では何らかの同期が必要です。まず第一に、あなたは(私はあなたの変数の正確な定義を知りませんが、主なアイデアは、あなたのケースでvolatileキーワードを使用することです)、次のように異なるスレッドで使用されるあなたの変数を実装する必要があります。揮発性

public static volatile bool isProcessActive; 
public static volatile int proccessCounter; 

キーワードは、あるスレッドで変数を使用するためのコンパイラ最適化をオフにします。変数がスレッドセーフであることを意味します。

System.Threading.TimerまたはSystem.Timers.Timerのいずれも使用しないでください。私は私の例で2番目のものを使用します。

public sealed class GenerateInvoice : 
{ 
    protected const int timerInterval = 1000; // define here interval between ticks 

    protected Timer timer = new Timer(timerInterval); // creating timer 

    public GenerateInvoice() 
    { 
     timer.Elapsed += Timer_Elapsed;  
    } 

    public void Start() 
    { 
     timer.Start(); 
    } 

    public void Stop() 
    { 
     timer.Stop(); 
    } 

    public void Timer_Elapsed(object sender, ElapsedEventArgs e) 
    {  
     try 
     { 
      DBBilling obj = new DBBilling(); 
      DataTable dtInvoiceID = obj.readData(@"SELECT * FROM (SELECT ird.BillByType, ird.InvoiceID, ir.BeginDate, ir.EndDate, ir.SendToQB, ir.SendEmail, 
       i.ARAccountID, i.ARAccountHotelID, i.invoiceNumber,i.[STATUS],UPDATETIME,row_number() over (PARTITION BY ird.INVOICEID ORDER BY UPDATETIME DESC) AS row_number 
       FROM Invoices i JOIN InvoicesRunRequestDetails ird ON ird.InvoiceID=i.InvoiceID 
       JOIN InvoicesRunRequest ir ON ird.RequestID = ir.RequestID 
       Where i.[STATUS] = 'PENDING') AS rows 
       WHERE ROW_NUMBER=1 ORDER BY UPDATETIME"); 

      processCounter = 0; 

      #region process 
      if (dtInvoiceID != null && dtInvoiceID.Rows.Count > 0) 
      { 
       //some code here.. 
      } 
      #endregion 
     } 
     catch (Exception ex)  //Mantis 1486 : WEBPMS1 Disk Space : 10 Aug 2016 
     { 
      log.ErrorFormat("Generate Invoice -> Process -> InnLink Billing Execute Query Exception. Error={0}", ex); 
      if(DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)) 
      { 
       processCounter++; 
       if (processCounter >= 1) //Need to change to 25 after Problem Solve 
       { 
        isProcessActive = false; 
        // supposing that log is a reference type and one of the solutions can be using lock 
        // in that case only one thread at the moment will call log.ErrorFormat 
        // but better to make synchronization stuff unside logger 
        lock (log) 
         log.ErrorFormat("Generate Invoice -> Process -> RunInvoice Service exiting loop"); //From here control is not going back     
       } 
       else 
        // if you need here some kind of execution sleep 
        // here you can stop timer, change it interval and run again 
        // it's better than use Thread.Sleep 

        // System.Threading.Thread.Sleep(5000); //Sleep for 5 Sec 
      } 
     }      
    } 
} 

TimerをベースにするGenerateReportのために同じアプローチを使用してください。

そして、最後に、あなたはあなたのOnStartOnStop方法を変更するので、のようなものが必要です。

protected GenerateInvoice generateInvoice; 
protected GenerateReport generateReport; 

protected override void OnStart(string[] args) 
{ 
    // all exception handling should be inside class 

    log.Debug("Starting Invoice Generation Service"); 

    generateInvoice = new GenerateInvoice(); 
    generateInvoice.Start(); 

    generateReport = new GenerateReport(); 
    generateReport.Start(); 
} 

protected override void OnStop() 
{ 
    generateInvoice.Stop(); 
    generateReport.Stop(); 
} 
+0

ありがとうございます!あなたのソリューションは本当に役に立ちます。答えとしてマーク!タイマーを使用して –

+0

は良い方法です。あなたのフィールドを揮発性としてマークするのは悪い習慣であり、お勧めしません。単にフィールドをスレッドセーフにするだけではなく、最新の値をもたらすように試み、プロセス内の他のスレッドを停止させる可能性があります。あなたのアクセスをロックする方がはるかに良い保証です。 http://stackoverflow.com/a/17530556/3090249 – gilmishal

1

回避するには、グローバル変数へのすべてのアクセスをロックするか、グローバル変数を使用しないでください。ここ

一の明白な例である

DBBilling.dbConnTimeoutErrorMessage.Any(ex.Message.Contains)

dbConnTimeoutErrorMessage 2つの異なるスレッドから使​​用して、私は

lock(locObj) 
{ 
    // access to dbConnTimeoutErrorMessage 
} 
とそれへの安全、サラウンドアクセスをスレッドされていないと想定されている静的フィールドであります

私は先に進んで、logもグローバル変数であると推測します。おそらく多分isProcessActiveまたはprocessCounterでさえあります。

これらのコメントにはさらに多くのものがあります。コードが2つの異なるスレッドで使用する前にスレッドセーフであることを確認してください。

私はあなたの問題を解決すると言いましたが、スレッドセーフなプログラミングの欠如は、必要なときにlockを使用しないという症状だと思います。秘密は、グローバルなコンテキストへのすべてのアクセスをロックすることです。

+0

提案していただきありがとうございます!私はこれを実装しようとします。私の場合、 'locObj'とは何ですか? –

+0

lockObjは単純な静的オブジェクトです(静的である必要はありませんが、2つの異なるスレッドに対して存在する必要があります)。単に 'new object()'で初期化してください。異なるグローバル変数に異なるlockObjectを使用するようにしてください。 詳細については、https://msdn.microsoft.com/en-us/library/mt679037.aspxを参照してください。 – gilmishal

関連する問題