2010-11-12 95 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等待完成事件..或任何逻辑,你要真。