任务并行库和长时间运行的任务

时间:2018-11-10 19:45:48

标签: c# .net-core task task-parallel-library background-service

我有一个BackgroundService(IHostedService),它实现ExecuteAsync,类似于Implement background tasks in microservices with IHostedService and the BackgroundService class中的示例。

我想运行多个任务,这些任务在外部系统中同时执行长时间运行的命令。我希望服务继续执行这些任务,直到服务停止-但我不想同时使用相同的参数执行命令(如果它与item.parameter1 = 123一起执行,我希望它等到123完成,然后再次执行123)。而且,它不应阻塞,也不应泄漏内存。如果发生异常,我想适当地停止有问题的任务,记录下来并重新启动。每次执行该命令都会获得不同的参数,因此如下所示:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    var items = GetItems(); //GetItems returns a List<Item>

    _logger.LogDebug($"ExternalCommandService is starting.");

    stoppingToken.Register(() => 
            _logger.LogDebug($" Execute external command background task is stopping."));

    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.LogDebug($"External command task is doing background work.");

        //Execute the command with values from the item
        foreach(var item in items)
        {
             ExecuteExternalCommand(item);
        }

        await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
    }

    _logger.LogDebug($"Execute external command background task is stopping.");

}

数据的结构非常简单:

public class MyData
{
    public string Foo { get; set; }
    public string Blee { get; set; }
}

在任务开发方面,我是一个新手,所以请原谅我缺乏理解。使ExecuteExternalCommand异步是否更有意义?我不确定这些任务是否并行执行。我究竟做错了什么?如何完成其​​他要求,例如异常处理和正常重启任务?请帮忙。

1 个答案:

答案 0 :(得分:1)

您有要按特定值分组的数据,然后反复在其自己的循环中运行。我将举一个执行此操作的方法的示例,希望您可以依靠它来获取答案所需的内容。

注意:根据如何模拟数据,此示例可能没有用。如果您在问题中提供正确的数据结构,我将更新答案以使其匹配。

您有数据。

public struct Data
{
    public Data(int value) => Value = value;
    public int Value { get; }
    public string Text => $"{nameof(Data)}: {Value}";
}

您希望按特定值对数据进行分组。 (注意:此示例可能不合逻辑,因为它已按Value对数据进行了分组,而该Linq已经是唯一的。例如,添加此代码:)

执行此操作的方法很多,但是在本示例中,我将使用DistinctIEqualityComparer<T>Data.Value比较public class DataDistinction : IEqualityComparer<Data> { public bool Equals(Data x, Data y) => x.Value == y.Value; public int GetHashCode(Data obj) => obj.Value.GetHashCode(); }

var dataItems = new List<Data>();

for (var i = 0; i < 10; i++)
{
    dataItems.Add(new Data(i));
}

模拟数据 对于此示例,我将模拟一些数据。

IEqualityComparer<T>

处理数据 对于此示例,我将使用Data通过Value唯一地获取每个private static void ProcessData(List<Data> dataItems) { var groupedDataItems = dataItems.Distinct(new DataDistinction()); foreach (var data in groupedDataItems) { LoopData(data); //We start unique loops here. } } 并开始处理。

Task

圈出唯一数据

在此示例中,我选择使用Task.Factory开始一个新的Action<object>。这将是一次使用它是有意义的,但通常您不需要它。 在此示例中,我传入了Task,其中object表示提供给state的状态或参数。 我还将解析您将看到的while并开始一个while循环。 CancellationTokenSource循环监视CancellationTokenSource的取消。为了方便起见,我在应用中静态声明了private static void LoopData(Data data) { Task.Factory.StartNew((state) => { var taskData = (Data)state; while (!cancellationTokenSource.IsCancellationRequested) { Console.WriteLine($"Value: {taskData.Value}\tText: {taskData.Text}"); Task.Delay(100).Wait(); } }, data, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } ;您将在底部的完整控制台应用程序中看到它。

Task

现在,我将其全部放入一个控制台应用程序中,您可以复制,粘贴和浏览该应用程序。

这将带您的数据,将其分解,然后无限期地全部运行using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Question_Answer_Console_App { class Program { private static readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); static void Main(string[] args) { var dataItems = new List<Data>(); for (var i = 0; i < 10; i++) { dataItems.Add(new Data(i)); } ProcessData(dataItems); Console.ReadKey(); cancellationTokenSource.Cancel(); Console.WriteLine("CANCELLING..."); Console.ReadKey(); } private static void ProcessData(List<Data> dataItems) { var groupedDataItems = dataItems.Distinct(new DataDistinction()); foreach (var data in groupedDataItems) { LoopData(data); } } private static void LoopData(Data data) { Task.Factory.StartNew((state) => { var taskData = (Data)state; while (!cancellationTokenSource.IsCancellationRequested) { Console.WriteLine($"Value: {taskData.Value}\tText: {taskData.Text}"); Task.Delay(100).Wait(); } }, data, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } ~Program() => cancellationTokenSource?.Dispose(); } public struct Data { public Data(int value) => Value = value; public int Value { get; } public string Text => $"{nameof(Data)}: {Value}"; } public class DataDistinction : IEqualityComparer<Data> { public bool Equals(Data x, Data y) => x.Value == y.Value; public int GetHashCode(Data obj) => obj.Value.GetHashCode(); } } //OUTPUT UNTIL CANCELLED //Value: 0 Text: Data: 0 //Value: 3 Text: Data: 3 //Value: 1 Text: Data: 1 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 5 Text: Data: 5 //Value: 6 Text: Data: 6 //Value: 7 Text: Data: 7 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 5 Text: Data: 5 //Value: 1 Text: Data: 1 //Value: 7 Text: Data: 7 //Value: 4 Text: Data: 4 //Value: 0 Text: Data: 0 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 6 Text: Data: 6 //Value: 5 Text: Data: 5 //Value: 3 Text: Data: 3 //Value: 8 Text: Data: 8 //Value: 4 Text: Data: 4 //Value: 1 Text: Data: 1 //Value: 2 Text: Data: 2 //Value: 9 Text: Data: 9 //Value: 7 Text: Data: 7 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 8 Text: Data: 8 //Value: 5 Text: Data: 5 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 1 Text: Data: 1 //Value: 4 Text: Data: 4 //Value: 9 Text: Data: 9 //Value: 7 Text: Data: 7 //Value: 0 Text: Data: 0 //Value: 6 Text: Data: 6 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 5 Text: Data: 5 //Value: 8 Text: Data: 8 //Value: 7 Text: Data: 7 //Value: 9 Text: Data: 9 //Value: 1 Text: Data: 1 //Value: 4 Text: Data: 4 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 5 Text: Data: 5 //Value: 7 Text: Data: 7 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 4 Text: Data: 4 //Value: 1 Text: Data: 1 //Value: 0 Text: Data: 0 //Value: 6 Text: Data: 6 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 1 Text: Data: 1 //Value: 9 Text: Data: 9 //Value: 5 Text: Data: 5 //Value: 8 Text: Data: 8 //Value: 7 Text: Data: 7 //Value: 4 Text: Data: 4 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 4 Text: Data: 4 //Value: 9 Text: Data: 9 //Value: 1 Text: Data: 1 //Value: 7 Text: Data: 7 //Value: 8 Text: Data: 8 //Value: 5 Text: Data: 5 //Value: 0 Text: Data: 0 //Value: 6 Text: Data: 6 //Value: 4 Text: Data: 4 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 5 Text: Data: 5 //Value: 7 Text: Data: 7 //Value: 9 Text: Data: 9 //Value: 8 Text: Data: 8 //Value: 1 Text: Data: 1 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 3 Text: Data: 3 //Value: 5 Text: Data: 5 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 7 Text: Data: 7 //Value: 1 Text: Data: 1 //Value: 0 Text: Data: 0 //Value: 6 Text: Data: 6 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 3 Text: Data: 3 //Value: 1 Text: Data: 1 //Value: 7 Text: Data: 7 //Value: 5 Text: Data: 5 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 // CANCELLING... ,直到程序结束。

Task

在注释中,您询问了await完成后如何写到控制台。在此示例中,您可以Task Task,并且在Data完成后,您可以打印该async void进行显示。不建议使用LoopData,但这是正确使用它的一次。

这是带有async await签名的更新后的private static async void LoopData(Data data) { await Task.Factory.StartNew((state) => { var taskData = (Data)state; while (!cancellationTokenSource.IsCancellationRequested) { Console.WriteLine($"Value: {taskData.Value}\tText: {taskData.Text}"); Task.Delay(100).Wait(); } }, data, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); Console.WriteLine($"Task Complete: {data.Value} : {data.Text}"); } //OUTPUT //Value: 0 Text: Data: 0 //Value: 1 Text: Data: 1 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 5 Text: Data: 5 //Value: 6 Text: Data: 6 //Value: 7 Text: Data: 7 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 1 Text: Data: 1 //Value: 5 Text: Data: 5 //Value: 4 Text: Data: 4 //Value: 7 Text: Data: 7 //Value: 9 Text: Data: 9 //Value: 8 Text: Data: 8 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 5 Text: Data: 5 //Value: 1 Text: Data: 1 //Value: 6 Text: Data: 6 //Value: 9 Text: Data: 9 //Value: 8 Text: Data: 8 //Value: 7 Text: Data: 7 //Value: 0 Text: Data: 0 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 1 Text: Data: 1 //Value: 4 Text: Data: 4 //Value: 5 Text: Data: 5 //Value: 9 Text: Data: 9 //Value: 6 Text: Data: 6 //Value: 7 Text: Data: 7 //Value: 8 Text: Data: 8 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 1 Text: Data: 1 //Value: 5 Text: Data: 5 //Value: 4 Text: Data: 4 //Value: 8 Text: Data: 8 //Value: 7 Text: Data: 7 //Value: 9 Text: Data: 9 //Value: 6 Text: Data: 6 //Value: 0 Text: Data: 0 //Value: 3 Text: Data: 3 //Value: 2 Text: Data: 2 //Value: 4 Text: Data: 4 //Value: 1 Text: Data: 1 //Value: 5 Text: Data: 5 //Value: 8 Text: Data: 8 //Value: 9 Text: Data: 9 //Value: 6 Text: Data: 6 //Value: 7 Text: Data: 7 //Value: 0 Text: Data: 0 //Value: 2 Text: Data: 2 //Value: 3 Text: Data: 3 //Value: 5 Text: Data: 5 //Value: 4 Text: Data: 4 //Value: 1 Text: Data: 1 //Value: 8 Text: Data: 8 //Value: 7 Text: Data: 7 //Value: 6 Text: Data: 6 //Value: 9 Text: Data: 9 // CANCELLING... //Task Complete: 0 : Data: 0 //Task Complete: 2 : Data: 2 //Task Complete: 3 : Data: 3 //Task Complete: 1 : Data: 1 //Task Complete: 5 : Data: 5 //Task Complete: 4 : Data: 4 //Task Complete: 8 : Data: 8 //Task Complete: 6 : Data: 6 //Task Complete: 7 : Data: 7 //Task Complete: 9 : Data: 9..

IndexOutOfBoundsException: