23

我将使用在我的应用程序上运行的大量任务。由于某种原因,每组任务都在运行。我想为这些任务命名,这样当我查看“并行任务”窗口时,我可以轻松识别它们。

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

如果有这样的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"

是否可以命名任务?

是否可以给 ‍‍‍<code>Parallel.ForEach 一个命名结构(可能使用 lambda),以便它使用该命名创建任务?

我错过了这样的API吗?


我还尝试使用继承的任务来覆盖它的 ToString()。但不幸的是,并行任务窗口不使用 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;
    }
}
4

11 回答 11

32

您可以将任何对象与任何对象联系起来。这是任务的扩展。它使用 Wea​​kReference,因此当所有引用都超出范围时,仍然可以对任务进行垃圾收集。

用法:

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

扩展类:

public static class TaskExtensions
{
    private static readonly Dictionary<WeakReference<Task>, object> TaskNames = new Dictionary<WeakReference<Task>, object>(); 

    public static void Tag(this Task pTask, object pTag)
    {
        if (pTask == null) return;
        var weakReference = ContainsTask(pTask);
        if (weakReference == null)
        {
            weakReference = new WeakReference<Task>(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<Task> 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;
    }
}
于 2015-12-22T14:54:48.957 回答
11

您不能真正命名 a Task,但您可以命名由 a 执行的方法,Task然后显示在并行任务窗口中。因此,如果命名Tasks 对您很重要,请不要使用 lambda,使用普通的命名方法。

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

于 2012-12-09T17:00:38.387 回答
3

你不能命名任务。

任务库在内部使用线程池,因此无法命名线程。你的继承方法也不起作用,因为像“.ContinueWith()”这样的方法总是会创建一个新任务,它不会从你的类继承。

于 2012-12-07T13:36:50.193 回答
2

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

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

或将您的任务映射到字典

        var mapping = new Dictionary<Task, string>();
        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}");
        }
于 2016-05-14T07:47:03.980 回答
1

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

于 2012-12-07T11:40:23.203 回答
1

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

于 2012-12-07T12:57:41.827 回答
0
public class NamesTask {
    readonly Queue<Task> _taskqueue = new Queue<Task>();
    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....");
                }
            }
        });
    }
}
于 2016-04-21T11:37:16.577 回答
0

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

这是我一直在做的一个示例:

private static void Main(string[] args)
{
    var tasksIdDic = new ConcurrentDictionary<int?, string>();
    Random rnd = new Random(DateTime.Now.Millisecond);
    var tasks = new List<Task>();

    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());
}

在此处输入图像描述

于 2018-07-25T08:02:39.977 回答
0

感谢迈克的回答,我最终得到了:

    public static class ExtensionMethods
    {
        private static readonly ConcurrentDictionary<WeakReference<Task>, object> TaskNames = new ConcurrentDictionary<WeakReference<Task>, object>();

        public static void _Tag(this Task pTask, object pTag)
        {
            if (pTask == null) return;
            var weakReference = ContainsTask(pTask) ?? new WeakReference<Task>(pTask);
            TaskNames[weakReference] = pTag;
        }
        public static void _Name(this Task pTask, string name)
        {
            _Tag(pTask, name);
        }

        public static object _Tag(this Task pTask)
        {
            var weakReference = ContainsTask(pTask);
            if (weakReference == null) return null;
            return TaskNames[weakReference];
        }
        public static object _Name(this Task pTask)
        {
            return (string)_Tag(pTask);
        }

        private static WeakReference<Task> ContainsTask(Task pTask)
        {
            foreach (var kvp in TaskNames.ToList())
            {
                WeakReference<Task> weakReference = kvp.Key;

                if (!weakReference.TryGetTarget(out var taskFromReference))
                {
                    TaskNames.TryRemove(weakReference, out _);
                    //TaskNames.TryRemove(out ); //Keep the dictionary clean.
                    continue;
                }

                if (pTask == taskFromReference)
                {
                    return weakReference;
                }
            }
            return null;
        }
    }

它现在是线程安全的,它还支持名称而不仅仅是标签。

于 2020-02-16T03:10:57.353 回答
0

如果这对任何人都有帮助,我将上述问题解决如下:

public static class NamedTasks
{
    public static Dictionary<string, Task> ActiveTasks { get; private set; } = new Dictionary<string, Task>();

    public static void Run(string name, Action action)
    {
        var task = new Task(action);
        ActiveTasks.Add(name, task);
        task.Start();

    public static void RemoveTask(string name)
    {
        if (ActiveTasks.ContainsKey(name))
            ActiveTasks.Remove(name);
    }
}

用法:

// Start new named task
var taskName = "<code file> - <method>";
NamedTasks.Run(taskName, () =>
{
    // do stuff
    NamedTasks.RemoveTask(taskName);
});

...

// Print names of active tasks
var taskNames = NamedTasks.ActiveTasks.Keys.ToList();
foreach (var taskName in taskNames)
    if (NamedTasks.ActiveTasks[taskName].Status == TaskStatus.Running)
        Console.WriteLine(taskName);

这对我来说效果很好。

于 2020-11-03T17:10:30.853 回答
-2
public class NamedTaskSchedular
{
    private static readonly ConcurrentDictionary<string, NamesTask> NamedTaskDictionary = new ConcurrentDictionary<string, NamesTask>();

    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);
    }
}
于 2016-04-21T11:39:31.557 回答