我有一个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异步是否更有意义?我不确定这些任务是否并行执行。我究竟做错了什么?如何完成其他要求,例如异常处理和正常重启任务?请帮忙。
答案 0 :(得分:1)
您有要按特定值分组的数据,然后反复在其自己的循环中运行。我将举一个执行此操作的方法的示例,希望您可以依靠它来获取答案所需的内容。
注意:根据如何模拟数据,此示例可能没有用。如果您在问题中提供正确的数据结构,我将更新答案以使其匹配。
您有数据。
public struct Data
{
public Data(int value) => Value = value;
public int Value { get; }
public string Text => $"{nameof(Data)}: {Value}";
}
您希望按特定值对数据进行分组。
(注意:此示例可能不合逻辑,因为它已按Value
对数据进行了分组,而该Linq
已经是唯一的。例如,添加此代码:)
执行此操作的方法很多,但是在本示例中,我将使用Distinct
,IEqualityComparer<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: