Как обрабатывать позиции в очереди, серийно на низкой приоритетной ните с использованием параллельных расширений
-
27-09-2019 - |
Вопрос
Я хочу знать, какой лучший способ обработать результаты длительного процесса, последовательно, но на низкой приоритетной ните с помощью .Net 4.0 Параллельные расширения.
У меня есть следующий класс, который оба делает выполнение и предоставляет результаты:
public class Step
{
public string Name { get; set; }
public TimeSpan Duration { get; set; }
public bool Completed { get; set; }
public void Execute()
{
DateTime before = DateTime.Now;
RemotedService.DoSomeStuff();
Duration = DateTime.Now - before;
Completed = true;
}
}
Как я могу обработать эти шаги, а также сохранить их в файл, в порядке, после того, как они обрабатываются? Я хотел бы сохранить их в файл, пока RemotedService.DoSomeStuff()
ждет ответа с сервера.
Написание файла будет такой:
using (StreamWriter w = File.AppendText("myFile.txt"))
{
var completedStep = completedSteps.Dequeue();
w.WriteLine(string.Format("Completed {0} with result: {1} and duration {2}",
completedStep.Name,
completedStep.Completed,
completedStep.Duration));
}
Один вариант, который приходит на ум, - это добавить их в Queue
и иметь А. Timer
это обрабатывает их. Но это не использует преимущества простоя от удаленных звонков.
Другой вариант, который приходит на ум, - это асинхронно написать каждый результат к файлу с помощью System.Threading.Tasks.Task
нами Step
, но это не гарантирует, что они будут спасены по порядку и могут также представить утверждение с записью в файл.
Решение
Я бы предложил создать BlockingCollection<Step>
(видеть System.Collections.Concurrent
пространство имен). Поскольку каждый шаг завершен, он добавляется к этой коллекции. Поведение по умолчанию BlockingCollection
это функционировать как очередь, так что вы получите поведение FIFO, которое вы ищете.
Служба второго потока очередь, удаляя каждый элемент и запись его в файл журнала.
Итак, если вы добавили очередь:
static private BlockingCollection<Step> LogQueue = new BlockingCollection<Step>();
Вы бы добавили это к своему Execute
Метод, после того, как элемент завершен:
LogQueue.Add(this);
И ваша ветка журнала, которую вы начнете в статическом конструкторе:
static void LoggingThread()
{
using (var w = new StreamWriter(...))
{
while (!LogQueue.IsCompleted())
{
Step item;
if (LogQueue.TryTake(out item))
{
w.WriteLine(....);
}
}
}
}
Поток журнала, как я написал, он использует System.Threading
нить. Там может быть легче или лучший способ сделать это с TPL. Я еще не ужасно знаком с TPL, поэтому я не мог сказать.
Другие советы
Один подход - создать пользовательский планировщик задач (см. http://msdn.microsoft.com/en-us/library/ee789351.aspx.). Ваш пользовательский планировщик задач может ограничить параллелизм к одному времени и обеспечить соблюдение строгого выполнения в порядке.
Создание планировщика задач также позволяет контролировать приоритет потока или квартиры, который может быть желательным в некоторых ситуациях.
Вы буквально описываете корпус использования для фонда рабочего процесса - все это делает для вас все эти вещи :)