如何在TPL中为任务指定名称

我将在我的应用程序上运行许多任务。 由于某种原因,每一堆任务都在运行。 我想命名这些任务,所以当我观看Parallel Tasks窗口时,我可以轻松识别它们。

从另一个角度来看,考虑我在框架级别使用任务来填充列表。 使用我的框架的开发人员也在为她的工作使用任务。 如果她查看并行任务窗口,她会发现一些不知道的任务。 我想命名任务,以便她可以将框架任务与她的任务区分开来。

如果有这样的API会非常方便:

var task = new Task(action, "Growth calculation task") 

或者可能:

 var task = Task.Factory.StartNew(action, "Populating the datagrid") 

甚至在使用Parallel.ForEach

 Parallel.ForEach(list, action, "Salary Calculation Task" 

可以命名任务吗?

是否有可能为Parallel.ForEach一个命名结构(可能使用lambda),因此它使用该命名创建任务?

我错过了某个地方的API吗?


我还尝试使用inheritance的任务来覆盖它的ToString()。 但不幸的是,Parallel Tasks窗口不使用ToString()!

 class NamedTask : Task { private string TaskName { get; set; } public NamedTask(Action action, string taskName):base(action) { TaskName = taskName; } public override string ToString() { return TaskName; } } 

您无法真正命名Task ,但您可以命名由Task执行的方法,然后在并行任务窗口中显示该方法。 因此,如果命名Task对您很重要,请不要使用lambdas,使用常规命名方法。

令人惊讶的是,即使Task没有直接执行您的方法,这甚至可以使用Parallel 。 我认为这是因为Parallel Tasks以某种方式从Parallel了解Task并以不同方式处理它们。

您可以将任何对象与任何对象相关联。 这是Task的扩展。 它使用WeakReference,因此当所有引用都超出范围时,仍可以对任务进行垃圾回收。

用法:

 var myTask = new Task(... myTask.Tag("The name here"); var nameOfTask = (string)myTask.Tag(); 

扩展类:

 public static class TaskExtensions { private static readonly Dictionary, object> TaskNames = new Dictionary, object>(); public static void Tag(this Task pTask, object pTag) { if (pTask == null) return; var weakReference = ContainsTask(pTask); if (weakReference == null) { weakReference = new WeakReference(pTask); } TaskNames[weakReference] = pTag; } public static object Tag(this Task pTask) { var weakReference = ContainsTask(pTask); if (weakReference == null) return null; return TaskNames[weakReference]; } private static WeakReference ContainsTask(Task pTask) { foreach (var kvp in TaskNames.ToList()) { var weakReference = kvp.Key; Task taskFromReference; if (!weakReference.TryGetTarget(out taskFromReference)) { TaskNames.Remove(weakReference); //Keep the dictionary clean. continue; } if (pTask == taskFromReference) { return weakReference; } } return null; } } 

你无法命名任务。

任务库在内部使用线程池,因此无法命名线程。 此外,您的inheritance方法将无法工作,因为像“.ContinueWith()”这样的方法将始终创建一个新任务,该任务不会从您的类inheritance。

我不认为你可以命名任务。 您可以使用Task.Id来跟踪任务。

因为我不知道并行任务窗口的行为,所以我在这里盲目但是如果它使用调试器api在NamedTask子类上添加DebuggerDisplay属性可能有帮助

如果您只需要在任务完成后知道任务的名称,那么您可以将其作为参数传递。 将其作为任务结果的一部分返回。

  private async Task MyTask(int x, string taskName) { return new[] { taskName, x.ToString() }; } 

或者将您的任务映射到字典

  var mapping = new Dictionary(); var task = new Task(() => Console.WriteLine("myNullTask")); mapping.Add(task, "myNullTask"); foreach (var taskX in mapping) { Console.WriteLine( $"Task Id: {taskX.Key.Id}, " + $"Task Name: {taskX.Value}, " + $"Task Status: {taskX.Key.Status}"); } 
 public class NamesTask { readonly Queue _taskqueue = new Queue(); private readonly object _queueLock = new object(); public Task RunTask(Action action) { //incoming task must be queued as soon as it arrives var inComingTask = new Task(action); lock (_queueLock) { _taskqueue.Enqueue(inComingTask); } return Task.Factory.StartNew(() => { //run all actions one by one.. while (true) { lock (_queueLock) { //only one task must be performed at a if (_taskqueue.Count == 0) return; var outTask = _taskqueue.Dequeue(); outTask.Start(); outTask.Wait(); Console.WriteLine("done...."); } } }); } } 

我想过有一本字典来帮助调试等等。

这是我一直在做的样本:

 private static void Main(string[] args) { var tasksIdDic = new ConcurrentDictionary(); Random rnd = new Random(DateTime.Now.Millisecond); var tasks = new List(); tasks.Add(Task.Run(() => { Task.Delay(TimeSpan.FromSeconds(rnd.Next(1, 5))).Wait(); tasksIdDic.TryAdd(Task.CurrentId, "First"); Console.WriteLine($"{tasksIdDic[Task.CurrentId]} completed."); })); tasks.Add(Task.Run(() => { Task.Delay(TimeSpan.FromSeconds(rnd.Next(1, 5))).Wait(); tasksIdDic.TryAdd(Task.CurrentId, "Second"); Console.WriteLine($"{tasksIdDic[Task.CurrentId]} completed."); })); tasks.Add(Task.Run(() => { Task.Delay(TimeSpan.FromSeconds(rnd.Next(1, 5))).Wait(); tasksIdDic.TryAdd(Task.CurrentId, "Third"); Console.WriteLine($"{tasksIdDic[Task.CurrentId]} completed."); })); //do some work - there is no guarantee, but assuming you add the task names to the dictionary at the very beginning of each thread, the dictionary will be populated and be of benefit sometime soon after the start of the tasks. //Task.Delay(TimeSpan.FromSeconds(5)).Wait(); //wait for all just so I see a console output Task.WaitAll(tasks.ToArray()); } 

在此处输入图像描述

 public class NamedTaskSchedular { private static readonly ConcurrentDictionary NamedTaskDictionary = new ConcurrentDictionary(); public static Task RunNamedTask(string name, Action action) { if (NamedTaskDictionary.ContainsKey(name)) { return NamedTaskDictionary[name].RunTask(action); } var task = new NamesTask(); NamedTaskDictionary[name] = task; return task.RunTask(action); } }