2010-11-12 3 views
2

У меня есть таймер, вызывающий функцию каждые 15 минут, эта функция подсчитывает количество строк в моем DGV и запускает поток для каждой строки (еще одной функции), указанный поток анализирует веб-страницу, которая может занимать от 1 секунды до 10 секунд до конца.Реализация очереди потоков/ожидания, как?

Несмотря на то, что он работает нормально, так как он имеет 1-6 строк, больше будет вызывать запросы на тайм-аут.

Я хочу, чтобы ждать, пока вновь созданный поток, чтобы закончить обработку перед возвращением в цикле, чтобы создать еще один поток без блокировки главного интерфейса

   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# дешифратора те, резьбонарезной инструмент

Спасибо за ваше время

+2

Если это так, почему вы используете новый поток и не вызываете метод? –

+0

Является ли ваш цикл for, как указано выше, работает в потоке пользовательского интерфейса? – CodingGorilla

+0

Если вы начинаете поток, а затем немедленно блокируете его, чтобы закончить, то вы ничего не выиграете, запустив нить. Вы можете просто вызвать 'UpdateLo()' непосредственно и избежать проблем. – Constantin

ответ

0

избежать UI замораживать рамки обеспечивают класс специально для этих целей: взглянуть на классе BackgroundWorker (выполняет операцию в отдельном потоке), вот несколько информации о: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx http://msdn.microsoft.com/en-us/magazine/cc300429.aspx

Btw выглядит, если я правильно понимаю, что вы не хотите распараллеливать какую-либо операцию, поэтому просто подождите, пока метод, обрабатывающий страницу, будет завершен. В основном для каждой строки (foreach look) вашей сетки вы получаете идентификатор и вызываете метод. Если вы хотите, чтобы идти параллельно просто повторно использовать один и тот же цикл по каждому элементу и добавьте сделать Parallel

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

0

вызова метода непосредственно или сделать какое-то время (со спящими вызовами), чтобы проверить состояние потока.

Есть также асинхронные события, но это вызовет другой метод, и вы хотите продолжить с той же точки.

0

Я понятия не имею, почему запросы будут тайм-аут. Это звучит как другая проблема. Тем не менее, я могу сделать несколько предложений относительно вашего текущего подхода.

  • Избегайте создания потоков в цикле с недетерминированными границами. В создании потоков много накладных расходов. Если количество операций неизвестно до начала, используйте вместо этого ThreadPool или Task Parallel Library.
  • Вы не получите желаемое поведение, заблокировав поток пользовательского интерфейса Thread.Join. Причина, по которой пользовательский интерфейс становится невосприимчивым, и эффективно выполняет сериализацию операций и отменяет любые преимущества, которые вы надеялись получить с помощью потоков.

Если вы действительно хотите ограничить количество параллельных операций, лучшим решением будет создание отдельного выделенного потока для запуска операций. Этот поток будет вращаться вокруг цикла на неопределенный срок, ожидая появления элементов в очереди, и когда они это сделают, они будут деактивированы и будут использовать эту информацию для асинхронного запуска операции (снова используя ThreadPool или TPL). Линия дезактивации может содержать логику ограничения количества одновременных операций. Найдите информацию о шаблоне производителя и потребителя, чтобы лучше понять, как вы можете это реализовать.

Существует немного кривая обучения, но кто сказал, что нарезка была легкой?

0

Рассмотрите возможность использования асинхронного CTP. Это асинхронный образец Microsoft, недавно выпущенный для скачивания. Это должно упростить асинхронное программирование. Ссылка: 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 минут).

EDIT
я исправил синтаксис выше, вы должны изменить UpdateLo взять объект вместо Int.

0

Если я правильно понимаю, что вы сейчас делаете, перекручивание через список идентификаторов в потоке пользовательского интерфейса, начиная новый поток для обработки каждого из них. Проблема блокировки, которую вы видите, вполне может заключаться в том, что для создания уникальных потоков требуется слишком много ресурсов. Так, лично (не зная больше) будет перепроектировать процесс следующим образом:

//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, чтобы дождаться завершенного события .. или любой другой логики, которую вы действительно хотите.

Смежные вопросы