Как назначить имя заданию в TPL - программирование
Подтвердить что ты не робот

Как назначить имя заданию в 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 структуру именования (возможно, используя лямбда), чтобы создать задачи с этим именем?

Есть ли такой API где-то, что мне не хватает?


Я также попытался использовать унаследованную задачу, чтобы переопределить ее 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;
    }
}
4b9b3361

Ответ 1

Вы действительно не можете назвать Task, но вы можете назвать метод, который выполняется Task, который затем отображается в окнах параллельных задач. Итак, если для вас важно присвоить имя Task, не используйте lambdas, используйте обычные именованные методы.

Удивительно, но это работает даже с Parallel, хотя Task не выполняет ваш метод напрямую. Я думаю, это связано с тем, что Parallel Tasks знает о Task из Parallel и обрабатывает их по-разному.

Ответ 2

Вы можете связать любой объект с любым объектом. Вот расширение для задачи. Он использует WeakReference, так что задача все равно может быть собрана в мусор, когда все ссылки выходят за рамки.

Использование:

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

Ответ 3

Вы не можете назвать задачи.

Библиотека задач внутренне использует пул потоков, поэтому нити не могут быть названы. Также ваш подход наследования не будет работать, потому что такие методы, как ".ContinueWith()", всегда будут создавать новую задачу, которая не будет наследовать от вашего класса.

Ответ 4

Я не думаю, что вы можете назвать задачи. Вы можете использовать Task.Id для отслеживания задач.

Ответ 5

Я снимаю здесь слепо, так как не знаю поведения Parallel Tasks, но если он использует отладчик api, добавив DebuggerDisplay атрибут вашего подкласса NamedTask может помочь

Ответ 6

Если вам нужно узнать имя задачи после завершения задачи, вы можете просто передать ее как параметр. Верните его как часть результата задачи.

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

Ответ 7

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....");
                }
            }
        });
    }
}

Ответ 8

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