2016-04-07 86 views
1

我是新來的多線程,我很難理解所有的方式來完成任務。我試圖在一個大的程序中實現它,但是沒有人想要毫無理由地查看所有數千行處理代碼,因此我編寫了一個簡單的測試程序,它使用Sleep()而不是做真正的工作,並使其變得非常簡單。Task.Factory.StartNew()或Task.Run()的正確用法?

問題是作爲底部。

using System; 
using System.Collections.Concurrent; 
using System.Threading.Tasks; 
using System.Threading; 
namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      BlockingCollection<int> col = new BlockingCollection<int>(); 
      for(int z = 0; z<=50; z++) 
      { 
       col.Add(z); 
      } 

      Console.WriteLine("Hi from main! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId); 
      var parent = Task.Factory.StartNew(() => 
      { 
       Console.WriteLine("Hi from PARENT! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId); 
       foreach (int num in col.GetConsumingEnumerable()) 
       { 
         Thread.Sleep(100); 
         Task.Factory.StartNew(() => 
         { 
          Thread.Sleep(num*100); 
          if (num == 50) 
          { 
           col.CompleteAdding(); //kick out of the foreach loop 
           Thread.Sleep(25000); 
          } 
          Console.WriteLine("Hi from a Child! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId); 

         }, TaskCreationOptions.AttachedToParent); 
       } 
       Console.WriteLine("Parent done making children. ThreadID: {0}", Thread.CurrentThread.ManagedThreadId); 
      }, TaskCreationOptions.LongRunning); 

      var final = parent.ContinueWith((antecedent) => 
      { 
       Console.WriteLine("DONE!"); 
      },TaskContinuationOptions.OnlyOnRanToCompletion 
       ); 

      Console.WriteLine("Calling Wait on final. ThreadID: {0}", Thread.CurrentThread.ManagedThreadId); 
      final.Wait(); 
      Console.ReadLine(); 
     } 
    } 
} 

該代碼將51個元素添加到阻塞集合,然後啓動一個名爲Parent的任務。 Parent將消耗阻塞集合中的項目,直到它被標記爲CompleteAdding()爲止。

父任務長時間運行,因爲我不希望它從線程池中拉出線程。然後,它應該產生將休眠一段時間的附加的子任務,如果元素是'50',則它調用CompleteAdding()

然後有一個名爲final的延續,只有在父項運行完成時纔會運行。如果阻塞收集也已完成並且所有子任務也完成,則父節點只能完成。

我然後調用從主final.Wait()(在我的實際程序的final.Wait()將有可能會在OnFormClosing()方法。)

做這一切的整點是爲了能夠確保該程序關閉所有當前之前運行工作結束,所有排隊工作也完成。

這是問題所在。我在很多地方看到我應該使用Task.Run()而不是Task.Factory.StartNew(),所以我嘗試將它們更改出來,然後我開始收到錯誤,說Error CS1643 Not all code paths return a value in lambda expression of type 'Func<Task>'爲什麼我需要返回值,如果我不想要返回。也是這個Task.Factory.StartNew()的正確使用還是我做一些危險/危險的事情?

note不知道它是否很重要,但在程序中,我打算在睡眠中使用這個設置,像匹配數據並將它發送到oracle數據庫。此外,我們不能高於.NET 4.6

+0

您不需要'BlockingCollection'來啓動50個任務,只需使用正常的for循環。哦,並**捕獲循環變量**。在任務運行時,「num」對於你所有的任務來說可能是50。 –

+0

在我看來,你試圖繞過TAP系統設計要做的事情,而是試圖使用「長時間運行的線程」自己重新創建它,因爲你不想從線程中拉出一個線程池。爲什麼?這正是TAP系統所做的,它會安排一大堆任務完成,然後確保它們全部完成。 – CodingGorilla

+0

我意識到這一點。目標不是開始50個任務,而是爲了確保將所有「工作」(在本例中爲int)從隊列中拉出,直到我們確信不會再添加任何內容,並且所有工作都在程序完成之前完成退出。但是最終所有應用都是一個工作項目對象,其中包含說明和數據,說明應該採取行動,並且在新數據可用時創建工作項目。可以在一個小時內完成50個項目,或者在20分鐘內完成50000個項目。沒有辦法知道。 – user5999614

回答

0

微軟的文檔描述Task.Run是異步運行任務的'更簡單的方法'。它和Task.Factory.StartNew都返回一個Task和語法是相同的。我有興趣看到給你編譯錯誤的代碼。但是,如果你的代碼與Task.Factory.StartNew一起工作,我沒有看到改變它的令人信服的理由。

 Task.Run(() => 
     { 
      Console.Write("Doing"); 
      Console.Write("Something"); 
     }); 

     Task.Factory.StartNew(() => 
     { 
      Console.Write("Doing"); 
      Console.Write("Something"); 
     }); 
+0

你可以複製粘貼我的代碼,只需將'Task.Factory.StartNew'更改爲'Task.Run',並且會重現錯誤。 – user5999614

0

讓我們簡化Task.Run版本的代碼:

Task.Run(() => { }, TaskCreationOptions.LongRunning); 

這段代碼是錯誤的:there is no overload of Task.Run() accepting TaskCreationOptions。但是C#編譯器報告的錯誤消息(「並非所有代碼路徑都返回'Func<Task>'類型的lambda表達式中的值」)也是錯誤的。我認爲這是C#編譯器中的一個錯誤,所以I reported it

相關問題