2016-04-27 3 views
1

私は以下のコードを持っていますが、私はそれが重要ではないと信じていますが、私は奇妙な動作をしています。ネストされたスレッド(タスク)Timing Out Premature

月を別々のスレッドで実行すると、うまく動作します(下の方法を参照してください)。しかし、私がマルチスレッドにすると(タスクのコメントを外して)、毎回タイムアウトします。タイムアウトは、月間5分、年間20分に設定され、1分以内にタイムアウトします。

既知の理由はありますか?私は何か簡単なものを逃しています

public List<PotentialBillingYearItem> GeneratePotentialBillingByYear() 
    { 
     var years = new List<PotentialBillingYearItem>(); 
     //var tasks = new List<Task>(); 
     var startYear = new DateTime(DateTime.Today.Year - 10, 1, 1); 
     var range = new DateRange(startYear, DateTime.Today.LastDayOfMonth()); 

     for (var i = range.Start; i <= range.End; i = i.AddYears(1)) 
     { 
      var yearDate = i; 
      //tasks.Add(Task.Run(() => 
      //{ 
       years.Add(new PotentialBillingYearItem 
       { 
        Total = GeneratePotentialBillingMonths(new PotentialBillingParameters { Year = yearDate.Year }).Average(s => s.Total), 
        Date = yearDate 
       }); 
      //})); 
     } 

     //Task.WaitAll(tasks.ToArray(), TimeSpan.FromMinutes(20)); 

     return years; 
    } 

    public List<PotentialBillingItem> GeneratePotentialBillingMonths(PotentialBillingParameters Parameters) 
    { 
     var items = new List<PotentialBillingItem>(); 
     var tasks = new List<Task>(); 
     var year = new DateTime(Parameters.Year, 1, 1); 
     var range = new DateRange(year, year.LastDayOfYear()); 

     range.Start = range.Start == range.End ? DateTime.Now.FirstDayOfYear() : range.Start.FirstDayOfMonth(); 

     if (range.End > DateTime.Today) range.End = DateTime.Today.LastDayOfMonth(); 

     for (var i = range.Start; i <= range.End; i = i.AddMonths(1)) 
     { 
      var firstDayOfMonth = i; 
      var lastDayOfMonth = i.LastDayOfMonth(); 
      var monthRange = new DateRange(firstDayOfMonth, lastDayOfMonth); 
      tasks.Add(Task.Run(() => 
      { 
       using (var db = new AlbionConnection()) 
       { 

        var invoices = GetInvoices(lastDayOfMonth); 

        var timeslipSets = GetTimeslipSets(); 

        var item = new PotentialBillingItem 
        { 
         Date = firstDayOfMonth, 
         PostedInvoices = CalculateInvoiceTotals(invoices.Where(w => w.post_date <= lastDayOfMonth), monthRange), 
         UnpostedInvoices = CalculateInvoiceTotals(invoices.Where(w => w.post_date == null || w.post_date > lastDayOfMonth), monthRange), 
         OutstandingDrafts = CalculateOutstandingDraftTotals(timeslipSets) 
        }; 

        items.Add(item); 
       } 
      })); 
     } 

     Task.WaitAll(tasks.ToArray(), TimeSpan.FromMinutes(5)); 

     return items; 
    } 
+1

1)あなたは本当に**この方法で、データベースにヒットしているスレッドの数に制限はありません。 'Parallel.'を使用し、並行性の最大度を設定します。 2) 'List <>'はスレッドセーフではありません。 PLINQ:https://msdn.microsoft.com/en-us/library/dd997425(v=vs.110).aspx –

+0

も参照してください。 'List <>'の呼び出しがうまくいきました。 –

答えて

0

さらに多くのスレッドプールスレッドを事前に割り当てることが考えられます。スレッドプールは新しいスレッドを割り当てるのが非常に遅いです。以下のコードは、スレッドプールスレッドの最小数を2.5kに設定して実行するのにわずか10秒(理論上の最小値)ですが、SetMinThreadsをコメントアウトすると1:30秒以上かかることになります。

static void Main(string[] args) 
{ 
    ThreadPool.SetMinThreads(2500, 10); 
    Stopwatch sw = Stopwatch.StartNew(); 
    RunTasksOutter(10); 
    sw.Stop(); 
    Console.WriteLine($"Finished in {sw.Elapsed}"); 
} 

public static void RunTasksOutter(int num) => Task.WaitAll(Enumerable.Range(0, num).Select(x => Task.Run(() => RunTasksInner(10))).ToArray()); 
public static void RunTasksInner(int num) => Task.WaitAll(Enumerable.Range(0, num).Select(x => Task.Run(() => Thread.Sleep(10000))).ToArray()); 

スレッドプールスレッドが不足している可能性もあります。 Per:https://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.110).aspxスレッドプール(タスクによって使用される)を使用しない時間帯は次のとおりです。

スレッドを長時間ブロックするタスクがあります。スレッドプールには最大数のスレッドがあるため、多数のブロックされたスレッドプールスレッドがタスクの開始を妨げる可能性があります。

これらのスレッドでIOが実行されているので、それらを非同期コードに置き換えるか、LongRunningオプションで開始することを検討してください。 https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcreationoptions(v=vs.110).aspx

+0

しかし、なぜ1分以内にタイムアウトになるのでしょうか? –

+1

スレッドプールは、新しいスレッドが作成される前に500msの遅延を追加します。プールは8つのスレッドから始まります。具体的には次のように書かれています。非常に短時間にいくつかのスレッドを起動して、2番目以降のタスクを実行する必要があります。スレッド管理戦略の一環として、スレッドプールはスレッドを作成する前に遅延します。したがって、短時間に多数のタスクがキューに入れられると、すべてのタスクが開始されるまでにかなりの遅延が生じる可能性があります。 –

+0

私はそれを理解していますが、 'GeneratePotentialBillingByYear()'にヒットした場合、関数には約32秒で失敗します。 –

関連する問題