[英]Parallel processing using TPL in windows service
我有一個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
{ }
}
}
我認為您的尋找將產生大量樣本。 我正在嘗試演示如何使用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
}
}
好的,很抱歉,我時間不多,但這是我作為替代品的一般想法/骨架。
如果我說實話,盡管我認為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;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.