我有一个Windows服务,该服务正在使用消息传递系统来获取消息。我还在Timer类的帮助下创建了一个回调机制,该机制可以帮助我在固定时间获取和处理消息后检查消息。以前,服务正在逐个处理消息。但是我希望消息到达后,处理机制可以并行执行。因此,如果第一条消息到达,则应继续处理一个任务,即使在使用回调方法配置的间隔时间之后,第一条消息的处理仍未完成(回调现在正在工作),也应选择下一条消息并对其进行处理另一个任务。
下面是我的代码:
Task.Factory.StartNew(() =>
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
});
public static void Process(Message message)
{
if (message != null)
{
// Processing logic
}
else
{
}
}
但是使用“任务工厂”时,我无法并行控制任务的数量,因此我想根据任务的可用性配置将在其上运行消息的任务的数量?
更新: 更新了我上面的代码以添加多个任务
下面是代码:
private static void Main()
{
try
{
int taskCount = 5;
Task.Factory.StartNewAsync(() =>
{
Subscriber<Message> consumer = new
Subcriber<Message>()
{
Interval = 1000
};
consumer.CallBack(Process, msg => msg!=
null);
}, taskCount);
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
public static void StartNewAsync(this TaskFactory
target, Action action, int taskCount)
{
var tasks = new Task[taskCount];
for (int i = 0; i < taskCount; i++)
{
tasks[i] = target.StartNew(action);
}
}
public static void Process(Message message)
{
if (message != null)
{
}
else
{ }
}
}
答案 0 :(得分:0)
我认为您的寻找将产生大量样本。我正在尝试演示如何使用ActionBlock<T>
来完成此操作。仍然有许多未知数,因此我将样本作为可以构建的骨架。在示例中,ActionBlock
将并行处理和处理从邮件系统收到的所有邮件
public class Processor
{
private readonly IMessagingSystem _messagingSystem;
private readonly ActionBlock<Message> _handler;
private bool _pollForMessages;
public Processor(IMessagingSystem messagingSystem)
{
_messagingSystem = messagingSystem;
_handler = new ActionBlock<Message>(msg => Process(msg), new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 5 //or any configured value
});
}
public async Task Start()
{
_pollForMessages = true;
while (_pollForMessages)
{
var msg = await _messagingSystem.ReceiveMessageAsync();
await _handler.SendAsync(msg);
}
}
public void Stop()
{
_pollForMessages = false;
}
private void Process(Message message)
{
//handle message
}
}
答案 1 :(得分:0)
好的,很抱歉,我的时间不多,但这是我作为替代品的一般想法/骨架。
如果我说实话,尽管我认为ActionBlock<T>
是更好的选择,但您要做的事太多了,唯一的限制是您无法动态扩展将要完成的工作量一次,尽管我认为限制可能会很高。如果您以这种方式进行操作,则可以拥有更多的控制权,或者只是动态地执行一定数量的任务,但是您必须手动执行许多操作,例如,如果要限制在某个时间点运行的任务数量时间,您必须实现一个排队系统(ActionBlock可以为您处理),然后对其进行维护。我想这取决于您收到的消息数量以及您的进程处理它们的速度。
您必须进行检查,并考虑如何将其应用于您的直接用例,因为我认为在并发包的想法方面,我这一方面有些粗略地实现了。
因此,我在此处提出的想法是,您可以启动任意数量的任务,或者使用集合将这些任务添加到正在运行的任务中,或者分别取消任务。
我想的主要事情是使Callback运行的方法触发执行工作的线程,而不是在单独的线程中进行订阅。
我和您一样使用Task.Factory.StartNew
,但是将返回的Task对象存储在对象(TaskInfo
)中,该对象也具有CancellationTokenSource,它的ID(在外部分配)作为属性,然后将其添加到TaskInfo
的集合,它是类的一个属性,它全部是以下内容的一部分:
已更新-为避免混淆,我刚刚更新了以前的代码。
您必须对其进行更新,并在诸如HeartbeatController
所需的内容之类的地方填充空白,以及因其超出问题范围而被调用的少数事件,但是这个想法是一样的。
public class TaskContainer
{
private ConcurrentBag<TaskInfo> Tasks;
public TaskContainer(){
Tasks = new ConcurrentBag<TaskInfo>();
}
//entry point
//UPDATED
public void StartAndMonitor(int processorCount)
{
for (int i = 0; i <= processorCount; i++)
{
Processor task = new Processor(ProcessorId = i);
CreateProcessorTask(task);
}
this.IsRunning = true;
MonitorTasks();
}
private void CreateProcessorTask(Processor processor)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task taskInstance = Task.Factory.StartNew(
() => processor.Start(cancellationTokenSource.Token)
);
//bind status update event
processor.ProcessorStatusUpdated += ReportProcessorProcess;
Tasks.Add(new ProcessorInfo()
{
ProcessorId = processor.ProcessorId,
Task = taskInstance,
CancellationTokenSource = cancellationTokenSource
});
}
//this method gets called once but the HeartbeatController gets an action as a param that it then
//executes on a timer. I haven't included that but you get the idea
//This method also checks for tasks that have stopped and restarts them if the manifest call says they should be running.
//Will also start any new tasks included in the manifest and stop any that aren't included in the manifest.
internal void MonitorTasks()
{
HeartbeatController.Beat(() =>
{
HeartBeatHappened?.Invoke(this, null);
List<int> tasksToStart = new List<int>();
//this is an api call or whatever drives your config that says what tasks must be running.
var newManifest = this.GetManifest(Properties.Settings.Default.ResourceId);
//task Removed Check - If a Processor is removed from the task pool, cancel it if running and remove it from the Tasks List.
List<int> instanceIds = new List<int>();
newManifest.Processors.ForEach(x => instanceIds.Add(x.ProcessorId));
var removed = Tasks.Select(x => x.ProcessorId).ToList().Except(instanceIds).ToList();
if (removed.Count() > 0)
{
foreach (var extaskId in removed)
{
var task = Tasks.FirstOrDefault(x => x.ProcessorId == extaskId);
task.CancellationTokenSource?.Cancel();
}
}
foreach (var newtask in newManifest.Processors)
{
var oldtask = Tasks.FirstOrDefault(x => x.ProcessorId == newtask.ProcessorId);
//Existing task check
if (oldtask != null && oldtask.Task != null)
{
if (!oldtask.Task.IsCanceled && (oldtask.Task.IsCompleted || oldtask.Task.IsFaulted))
{
var ex = oldtask.Task.Exception;
tasksToStart.Add(oldtask.ProcessorId);
continue;
}
}
else //New task Check
tasksToStart.Add(newtask.ProcessorId);
}
foreach (var item in tasksToStart)
{
var taskToRemove = Tasks.FirstOrDefault(x => x.ProcessorId == item);
if (taskToRemove != null)
Tasks.Remove(taskToRemove);
var task = newManifest.Processors.FirstOrDefault(x => x.ProcessorId == item);
if (task != null)
{
CreateProcessorTask(task);
}
}
});
}
}
//UPDATED
public class Processor{
private int ProcessorId;
private Subsriber<Message> subsriber;
public Processor(int processorId) => ProcessorId = processorId;
public void Start(CancellationToken token)
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
}
private void Process()
{
//do work
}
}
希望这可以让您了解如何解决您的问题,而我并没有忘记要点:)。
更新
要使用事件更新进度或正在处理的任务,我将其提取到自己的类中,该类上具有订阅方法,并在创建该类的新实例时,将该事件分配给父类,然后可以更新您的UI或您要使用该信息执行的任何操作。
所以Process()
的内容看起来像这样:
Processor processor = new Processor();
Task task = Task.Factory.StartNew(() => processor.ProcessMessage(cancellationTokenSource.CancellationToken));
processor.StatusUpdated += ReportProcess;