лучшие варианты, чем ручной словарь для ссылки на «текущую задачу», внутри которой выполняется метод? - PullRequest
4 голосов
/ 25 марта 2012

ПРИМЕЧАНИЕ: я не говорю, что это хорошая идея, я просто пытаюсь выяснить, есть ли «лучший» вариант, чем этот метод грубой силы.

Это возникло в предыдущем потоке SO @ Как получить ссылку на текущую задачу?

Однако этот поток был немного более ограничен конкретным интерфейсом.

Подход грубой силы, который я использовал вместе, просто использует словарь слабых ссылок.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace GetCurrentTaskExample
{
    public static class TaskContext
    {
        // don't need a ConcurrentDictionary since we won't be reading/writing the same task id with different tasks concurrently
        private static readonly Dictionary<int, WeakReference> s_indexedTaskReferences = new Dictionary<int, WeakReference>();

        public static void AddAndStartTasks(IEnumerable<Task> tasks)
        {
            foreach (var task in tasks)
            {
                AddTask(task);
                task.Start();
            }
        }

        public static void AddTask(Task task)
        {
            s_indexedTaskReferences[task.Id] = new WeakReference(task);
        }

        public static Task GetCurrentTask()
        {
            var taskId = Task.CurrentId;
            if (taskId == null) return null;

            WeakReference weakReference;
            if (s_indexedTaskReferences.TryGetValue(taskId.Value, out weakReference) == false) return null;
            if (weakReference == null) return null; // should not happen since we don't store null as a value

            var task = weakReference.Target as Task;
            return task;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var tasks = Enumerable.Range(0, 100)
                .Select(i => new Task(VerifyCurrentTaskWorks, i))
                .ToArray();

            TaskContext.AddAndStartTasks(tasks);

            Task.WaitAll(tasks);
        }

        static void VerifyCurrentTaskWorks(object instanceIdentifier)
        {
            var currentTask = TaskContext.GetCurrentTask();

            if (currentTask.Id == Task.CurrentId)
            {
                Console.WriteLine("Verified for instance {0} that Task.CurrentId value of {1} matches Id property {2} of task {3}",
                                  instanceIdentifier, Task.CurrentId, currentTask.Id, currentTask);
            }
            else
            {
                var errorMessage = String.Format("TaskContext.GetCurrentTask() failed for instance {0} with Task.CurrentId value of {1} and currentTask.Id value of {2}",
                                  instanceIdentifier, Task.CurrentId, currentTask.Id);
                throw new InvalidOperationException(errorMessage);
            }
        }
    }
}

Однако это явно означает, что все, что создает задачи, вынуждено справляться с этой дополнительной головной болью, поэтому оно не очень удобно, особенно асинхронные методы WRT C # 5, где задача не так явно создана.

Опять же, вероятно, плохая идея иметь код, который нуждается в этом, так что считайте его более похожим на упражнение на мысль. :)

1 Ответ

0 голосов
/ 18 апреля 2012

К сожалению, не так уж много лучшего способа, и, поскольку конечной целью было отключить Task.CurrentId, это действительно даже не полезно, так как у нас не будет возможности получить «текущий идентификатор задачи» для асинхронных методов.(после первого ожидания, так что оно уже возвращено вызывающей стороне).

...