2016-03-04 83 views
0

我仍然在使用異步&多線程加快速度。我試圖監視何時啓動任務仍在運行(在UI中顯示)。然而,即使當我認爲它的Status仍然在運行時,它表明它比我想要的更早地等待RanToCompletion。任務在等待時仍被標記爲RanToCompletion,當仍然運行時

這是我正在做的示例。這一切似乎都圍繞着等待。當它等待時,它被標記爲RanToCompletion。

我要跟蹤其開始這一切,在某種程度上主要任務的指示,我認爲它仍在運行一路到底只有RanToCompletion當它全部完成,其中包括回購電話和WhenAll。

如何更改此項以獲取我想要的關於tskProdSeeding任務狀態的反饋?

我的控制檯應用程序main方法調用此:

Task tskProdSeeding; 
tskProdSeeding = Task.Factory.StartNew(SeedingProd, _cts.Token); 

哪個運行此:

private async void SeedingProd(object state) 
{ 
    var token = (CancellationToken)state; 

    while (!token.IsCancellationRequested) 
    { 
     int totalSeeded = 0; 

     var codesToSeed = await _myRepository.All().ToListAsync(token); 

     await Task.WhenAll(Task.Run(async() => 
     { 
      foreach (var code in codesToSeed) 
      { 
       if (!token.IsCancellationRequested) 
       { 
        try 
        { 
         int seedCountByCode = await _myManager.SeedDataFromLive(code); 

         totalSeeded += seedCountByCode; 
        } 
        catch (Exception ex) 
        { 
         _logger.InfoFormat(ex.ToString()); 
        } 
       } 
      } 
     }, token)); 

     Thread.Sleep(30000); 
    } 
} 

回答

2

如果使用async void外任務不能告訴當任務完成後,您需要改爲使用async Task

其次,一旦你切換到async TaskTask.Factory.StartNew無法處理返回Task功能,你需要切換到Task.Run(

tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token); 

一旦你這樣做這兩方面的變化,你將能夠等待或者在tskProdSeeding上做一個.Wait(),它會在繼續之前等待所有工作完成。

請參閱「Async/Await - Best Practices in Asynchronous Programming」瞭解更多關於不做async void

請閱讀「StartNew is Dangerous」以瞭解更多關於爲什麼您不應該使用StartNew的方式使用它。


P.S.在SeedingProd中,您應該將其切換爲使用await Task.Delay(30000);插入的Thread.Sleep(30000);,然後在等待時不會鎖定線程。如果你這樣做,你可能會掉落

tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token); 

,並只讓

tskProdSeeding = SeedingProd(_cts.Token); 

因爲功能沒有不再有它內部的阻塞調用。

0

當您在一個方法上指定async時,它會編譯成一個帶有任務的狀態機,因此SeedingProd不會同步運行,但是即使返回void,也會充當Task。所以當你打電話給Task.Factory.StartNew(SeedingProd)時,你就開始了一個開始另一項任務的任務 - 這就是爲什麼第一個任務在第二個任務之前完成的原因。所有你需要做的就是添加Task返回參數,而不是void

private async Task SeedingProdAsync(CancellationToken ct) 
{ 
... 
} 

和簡單,因爲這稱呼它:

Task tskProdSeeding = SeedingProdAsync(_cts.Token); 
+0

異步函數同步運行,直到第一個「await」在您調用它的時候沒有完成爲止。正因爲如此,只要刪除任務工廠是有風險的,他仍然具有'Thread.Sleep(30000);',如果'ToListAsync'和'WhenAll'同步完成,則會導致調用'SeedingProdAsync'的線程阻塞30秒,如果你要下降工廠(而不是用'Taks.Run('替換它),你必須換出睡了'Task.Delay'。 –

+0

我敢打賭'Thread.Sleep'剛剛添加爲測試跟蹤完成,並不是真正的代碼的一部分 –

+0

我不這麼認爲,它是在一個'while'循環,我敢打賭,睡眠在那裏,因爲他希望任務每30秒運行一次直到它被取消爲止 –

1

我不相信你需要第二個線程(Task.Run或者StartNew)。它看起來像大部分工作是I/O密集型的,如果你正在做異步,並使用Task.Delay代替Thread.Sleep,然後there is no thread通過這些操作消耗你的UI不應該凍結。人們首先是新來異步需要明白的是,它是不一樣的東西多線程。後者是所有關於消耗更多的線程,前者是所有關於消耗少。着眼於消除阻擋,你不應該需要一個第二個線程。

正如其他人所指出的,SeedingProd需要返回Task而不是void,因此您可以觀察其完成情況。我相信你的方法可以簡化爲:

private async Task SeedingProd(CancellationToken token) 
{ 
    while (!token.IsCancellationRequested) 
    { 
     int totalSeeded = 0; 

     var codesToSeed = await _myRepository.All().ToListAsync(token); 

     foreach (var code in codesToSeed) 
     { 
      if (token.IsCancellationRequested) 
       return; 

      try 
      { 
       int seedCountByCode = await _myManager.SeedDataFromLive(code); 
       totalSeeded += seedCountByCode; 
      } 
      catch (Exception ex) 
      { 
       _logger.InfoFormat(ex.ToString()); 
      } 
     } 

     await Task.Dealy(30000); 
    } 
} 

然後只需調用該方法,不用等待,就可以完成任務。

Task mainTask = SeedingProd(token); 
相關問題