2013-11-28 2 views
1

мне нужно обработать запрос пользователя по одному (так же как работа очереди)Создание очереди темы

Это то, что я есть:

Thread checkQueue; 
Boolean IsComplete = true; 

protected void Start() 
{ 
    checkQueue = new Thread(CheckQueueState); 
    checkQueue.Start();  
} 

private void CheckQueueState() 
    { 
     while (true) 
     { 
      if (checkIsComplete) 
      { 
       ContinueDoSomething(); 

       checkQueue.Abort(); 
       break; 
      } 
      System.Threading.Thread.Sleep(1000); 
     } 
    } 

protected void ContinueDoSomething() 
{ 
    IsComplete = false; 
    ... 
    ... 
    IsComplete = true; //when done, set it to true 
} 

Everytime когда новый запрос от пользователя, система вызовет функцию Start() и проверит, завершено ли предыдущее задание, если да, то перейдет к следующему заданию.

Но я не уверен, правильно ли это сделать.

Любое улучшение или любой лучший способ сделать это?

+0

Вы эффективно не можете использовать Thread.Abort. Найдите на SO, чтобы найти некоторые дискуссии о том, почему. – usr

+0

Вы действительно хотите сделать это в ASP.NET? Это похоже на то, что работает на сервере и работает независимо от клиентских браузеров. –

+0

@usr Ok. Спасибо за ваш совет. – My2ndLovE

ответ

1

Мне нравится предложение usr относительно использования потока данных TPL. Если у вас есть возможность добавлять внешние зависимости к вашему проекту (поток данных TPL не распространяется как часть платформы .NET), то он обеспечивает чистое решение вашей проблемы.

Если вы придерживаетесь того, что предлагает инфраструктура, вы должны взглянуть на BlockingCollection<T>, который прекрасно работает с шаблоном производителя-потребителя, который вы пытаетесь реализовать.

Я собрал быстрый пример .NET 4.0, чтобы проиллюстрировать, как его можно использовать в вашем сценарии. Он не очень тощий, потому что у него много звонков Console.WriteLine(). Однако, если вы вынимаете весь беспорядок, это очень просто.

В центре этого является BlockingCollection<Action>, который получает Action делегатов добавили к нему из любого потока, и поток специально предназначенный для выполнения, и извлечения из очереди тех, Action сек последовательно в том порядке, в котором они были добавлены.

using System; 
using System.Collections.Concurrent; 
using System.Threading; 
using System.Threading.Tasks; 

namespace SimpleProducerConsumer 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Main thread id is {0}.", Thread.CurrentThread.ManagedThreadId); 

      using (var blockingCollection = new BlockingCollection<Action>()) 
      { 
       // Start our processing loop. 
       var actionLoop = new Thread(() => 
       { 
        Console.WriteLine(
         "Starting action loop on thread {0} (dedicated action loop thread).", 
         Thread.CurrentThread.ManagedThreadId, 
         Thread.CurrentThread.IsThreadPoolThread); 

        // Dequeue actions as they become available. 
        foreach (var action in blockingCollection.GetConsumingEnumerable()) 
        { 
         // Invoke the action synchronously 
         // on the "actionLoop" thread. 
         action(); 
        } 

        Console.WriteLine("Action loop terminating."); 
       }); 

       actionLoop.Start(); 

       // Enqueue some work. 
       Console.WriteLine("Enqueueing action 1 from thread {0} (main thread).", Thread.CurrentThread.ManagedThreadId); 
       blockingCollection.Add(() => SimulateWork(1)); 

       Console.WriteLine("Enqueueing action 2 from thread {0} (main thread).", Thread.CurrentThread.ManagedThreadId); 
       blockingCollection.Add(() => SimulateWork(2)); 

       // Let's enqueue it from another thread just for fun. 
       var enqueueTask = Task.Factory.StartNew(() => 
       { 
        Console.WriteLine(
         "Enqueueing action 3 from thread {0} (task executing on a thread pool thread).", 
         Thread.CurrentThread.ManagedThreadId); 

        blockingCollection.Add(() => SimulateWork(3)); 
       }); 

       // We have to wait for the task to complete 
       // because otherwise we'll end up calling 
       // CompleteAdding before our background task 
       // has had the chance to enqueue action #3. 
       enqueueTask.Wait(); 

       // Tell our loop (and, consequently, the "actionLoop" thread) 
       // to terminate when it's done processing pending actions. 
       blockingCollection.CompleteAdding(); 

       Console.WriteLine("Done enqueueing work. Waiting for the loop to complete."); 

       // Block until the "actionLoop" thread terminates. 
       actionLoop.Join(); 

       Console.WriteLine("Done. Press Enter to quit."); 
       Console.ReadLine(); 
      } 
     } 

     private static void SimulateWork(int actionNo) 
     { 
      Thread.Sleep(500); 
      Console.WriteLine("Finished processing action {0} on thread {1} (dedicated action loop thread).", actionNo, Thread.CurrentThread.ManagedThreadId); 
     } 
    } 
} 

И выход:

0.016s: Main thread id is 10. 
0.025s: Enqueueing action 1 from thread 10 (main thread). 
0.026s: Enqueueing action 2 from thread 10 (main thread). 
0.027s: Starting action loop on thread 11 (dedicated action loop thread). 
0.028s: Enqueueing action 3 from thread 6 (task executing on a thread pool thread). 
0.028s: Done enqueueing work. Waiting for the loop to complete. 
0.527s: Finished processing action 1 on thread 11 (dedicated action loop thread). 
1.028s: Finished processing action 2 on thread 11 (dedicated action loop thread). 
1.529s: Finished processing action 3 on thread 11 (dedicated action loop thread). 
1.530s: Action loop terminating. 
1.532s: Done. Press Enter to quit. 
+0

Спасибо за подробное объяснение. Одним из требований является то, что он должен выполняться один за другим (для выполнения каждой задачи может потребоваться 30 секунд). Как я могу убедиться, что текущая задача будет выполнена до запуска следующей задачи? – My2ndLovE

+0

@ My2ndLovE, я знал об этом требовании. Я префиксные выходные строки с меткой времени, чтобы вы могли видеть, что поставленные в очередь задания фактически выполняются последовательно (каждый занимает ~ 500 мс). Ваше использование слова «задача» немного смутило меня. * Вы используете 'Task' для инкапсуляции вашей работы? Потому что, если вы используете 'ConcurrentExclusiveSchedulerPair'' ExclusiveScheduler', то лучше всего разбираться в этом. –

0

Используйте ActionBlock<T> из библиотеки потока данных TPL. Установите MaxDegreeOfParalellism на 1, и все готово.

Обратите внимание, что рабочие процессы ASP.NET могут быть перезагружены в любой момент (например, из-за запланированной перезагрузки, ограничения памяти, перезагрузки или развертывания сервера), поэтому работа в очереди может быть внезапно потеряна без уведомления. Я рекомендую вам заглянуть в какое-то внешнее решение для очередей, такое как MSMQ (или другие) для надежных очередей.

+0

ActionBlock необходимо запустить в .net framework 4.5? Я использую vs2010 – My2ndLovE

+0

Он доступен как библиотека уже давно. Это требует не более 4,0. – usr

0

Взгляните на Reactive Extensions от Microsoft. Эта библиотека содержит набор доступных планировщиков, которые следуют семантике, которая вам нужна.

Лучшее, что подходит вашим потребностям, это EventLoopScheduler. Он будет стоять в очереди и выполнять их один за другим. Если он завершает действие, и в очереди будет больше элементов, он будет последовательно обрабатывать действия в одном потоке, пока очередь не будет пустой, а затем будет располагать поток. Когда новое действие ставится в очередь, он создает новый поток. Из-за этого он очень эффективен.

код супер просто и выглядит следующим образом:

var scheduler = new System.Reactive.Concurrency.EventLoopScheduler(); 

scheduler.Schedule(() => { /* action here */ }); 

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

var scheduler = new System.Reactive.Concurrency.NewThreadScheduler(); 

scheduler.Schedule(() => { /* action here */ }); 

Очень простой.

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