2010-11-12 75 views
2

我有一個計時器每15分鐘調用一次函數,這個函數計算我的DGV中線的數量,併爲每行(另一個函數)啓動一個線程,表示線程解析一個網頁,可能需要1秒到10秒完成。實現一個線程隊列/等待,如何?

雖然它工作正常,因爲它是1-6行,不再會導致請求超時。

我希望它找回在循環不鎖定主UI

   for (int x = 0; x <= dataGridFollow.Rows.Count - 1; x++) 
       { 
        string getID = dataGridFollow.Rows[x].Cells["ID"].Value.ToString(); 
        int ID = int.Parse(getID); 
        Thread t = new Thread(new ParameterizedThreadStart(UpdateLo)); 
        t.Start(ID); 
        // <- Wait for thread to finish here before getting back in the for loop 
       } 

我已經在過去24小時內一派很多創建另一個線程之前等待新創建的線程完成處理,閱讀了很多關於這個特定問題及其實現(Thread.Join,ThreadPools,Queuing,甚至SmartThreadPool)。

這可能是我讀過的正確答案的地方,但我不放心足以與C#來正是DeCypher那些線程工具

感謝您的時間

+2

如果你爲什麼要使用一個新的線程,而不是調用一個方法,多數民衆贊成的情況下? – 2010-11-12 14:25:33

+0

您的for循環如上所列,在UI線程上運行? – CodingGorilla 2010-11-12 14:25:58

+0

如果你開始一個線程,然後立即阻止等待它完成,那麼你不會通過啓動線程獲得任何東西。您可以直接調用'UpdateLo()'並避免麻煩。 – Constantin 2010-11-12 14:39:45

回答

0

避免UI凍結框架爲這些目的提供一類明確:有一個看看BackgroundWorker的類(在單獨的線程執行的操作),這裏的一些相關信息:http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx http://msdn.microsoft.com/en-us/magazine/cc300429.aspx

順便說一句看起來我是否理解正確,你不想並行任何操作,只是等待解析頁面的方法完成。基本上每個(foreach)看你的網格行你得到的ID和調用方法。如果你想要去的平行只是重複使用相同的foreach循環,並添加使其並行

http://msdn.microsoft.com/en-us/library/dd460720.aspx

0

調用一個方法直接或做一段時間循環(帶睡眠呼叫)來檢查線程的狀態。

也有異步事件,但會調用另一種方法,並且你想從同一點繼續。

0

我不知道爲什麼請求會超時。這聽起來像是一個不同的問題。不過,我可以就您目前的方法提出一些建議。

  • 避免在具有非確定性邊界的循環中創建線程。創建線程有很多開銷。如果之前未知道操作次數,請改爲使用ThreadPoolTask Parallel Library
  • 您不會通過屏蔽UI線程Thread.Join來獲得您想要的行爲。 UI變得無響應的原因,它將有效地將操作序列化並取消你希望通過線程獲得的任何優勢。

如果你真的想限制併發操作的數量,那麼更好的解決方案是創建一個單獨的專用線程來啓動操作。這個線程將無限期地循環一個循環,等待項目出現在隊列中,當他們這樣做時,它將使它們出列並使用該信息異步啓動操作(再次使用ThreadPool或TPL)。出隊線程可以包含限制併發操作數的邏輯。搜索有關生產者 - 消費者模式的信息,以更好地瞭解如何實現這一點。

有一點學習曲線,但誰說線程是很容易的權利?

0

考慮使用異步CTP。這是微軟最近發佈的下載的異步模式。它應該極大地簡化異步編程。鏈接是http://msdn.microsoft.com/en-us/vstudio/async.aspx。 (先閱讀白皮書)

您的代碼如下所示。 (我還沒有驗證過我的語法,對不起)。

private async Task DoTheWork() 
{ 
    for(int x = 0; x <= dataGridFollow.Rows.Count - 1; x++) 
    { 
     string getID = dataGridFollow.Rows[x].Cells["ID"].Value.ToString(); 
     int ID = int.Parse(getID); 
     task t = new Task(new Action<object>(UpdateLo), ID); 
     t.Start(); 
     await t; 
    } 
} 

此方法返回一個任務,可以定期檢查完成。這遵循「火與遺忘」的模式,意思就是你稱之爲「大火與遺忘」的模式,大概你不關心它何時完成(只要它在15分鐘前完成)。

編輯
我糾正上面的語法,你需要改變UpdateLo採取的對象,而不是一個int。

0

如果我理解正確的話,你當前做的是通過在UI線程ID列表循環,起動一個新的線程來處理每一個。你所看到的阻塞問題很可能是它需要太多的資源來創建獨特的線程。所以,個人(不知道更多)將重新設計的過程中,像這樣:

//Somewhere in the UI Thread 
Thread worker = new Thread(new ParameterizedThreadStart(UpdateLoWorker)); 
worker.Start(dataGridFollow.Rows); 

//worker thread 
private void UpdateLoWorker(DataRowCollection rows) 
{ 
    foreach(DataRow r in rows){ 
     string getID = r.Cells["ID"].Value.ToString(); 
     int ID = int.Parse(getID); 
     UpdateLo(ID); 
    } 
} 

在這裏,你就會有一個無阻塞的工人,其順序處理每個ID。

0

你想要的是掀起幾個工人是做一些任務。

當一個人完成,你可以開始新的一個關。

我敢肯定有使用線程池或什麼更好的辦法..但我很無聊,所以我這個走了過來。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.ComponentModel; 
using System.Threading; 

namespace WorkerTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      WorkerGroup workerGroup = new WorkerGroup(); 

      Console.WriteLine("Starting..."); 

      for (int i = 0; i < 100; i++) 
      { 
       var work = new Action(() => 
       { 
        Thread.Sleep(1000); //somework 
       }); 

       workerGroup.AddWork(work); 
      } 

      while (workerGroup.WorkCount > 0) 
      { 
       Console.WriteLine(workerGroup.WorkCount); 
       Thread.Sleep(1000); 
      } 

      Console.WriteLine("Fin"); 

      Console.ReadLine(); 
     } 
    } 


    public class WorkerGroup 
    { 
     private List<Worker> workers; 

     private Queue<Action> workToDo; 

     private object Lock = new object(); 

     public int WorkCount { get { return workToDo.Count; } } 

     public WorkerGroup() 
     { 
      workers = new List<Worker>(); 
      workers.Add(new Worker()); 
      workers.Add(new Worker()); 

      foreach (var w in workers) 
      { 
       w.WorkCompleted += (OnWorkCompleted); 
      } 

      workToDo = new Queue<Action>(); 
     } 

     private void OnWorkCompleted(object sender, EventArgs e) 
     { 
      FindWork(); 
     } 

     public void AddWork(Action work) 
     { 
      workToDo.Enqueue(work); 
      FindWork(); 
     } 

     private void FindWork() 
     { 
      lock (Lock) 
      { 
       if (workToDo.Count > 0) 
       { 
        var availableWorker = workers.FirstOrDefault(x => !x.IsBusy); 
        if (availableWorker != null) 
        { 
         var work = workToDo.Dequeue(); 
         availableWorker.StartWork(work); 
        } 
       } 
      } 
     } 
    } 

    public class Worker 
    { 
     private BackgroundWorker worker; 

     private Action work; 

     public bool IsBusy { get { return worker.IsBusy; } } 

     public event EventHandler WorkCompleted; 

     public Worker() 
     { 
      worker = new BackgroundWorker(); 
      worker.DoWork += new DoWorkEventHandler(OnWorkerDoWork); 
      worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OnWorkerRunWorkerCompleted); 
     } 

     private void OnWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      if (WorkCompleted != null) 
      { 
       WorkCompleted(this, EventArgs.Empty); 
      } 
     } 

     public void StartWork(Action work) 
     { 
      if (!IsBusy) 
      { 
       this.work = work; 
       worker.RunWorkerAsync(); 
      } 
      else 
      { 
       throw new InvalidOperationException("Worker is busy"); 
      } 
     } 

     private void OnWorkerDoWork(object sender, DoWorkEventArgs e) 
     { 
      work.Invoke(); 
      work = null; 
     } 
    } 
} 

這只是一個起點。

您可以用行動清單開始其關閉,然後當有該組動作完成了對一個完整的事件。

那麼至少你可以使用一個ManualResetEvent等待完成事件..或任何邏輯,你要真。