NOTE: I’m not saying this is a good idea, just trying to find out if there’s a ‘better’ option than this brute-force one.
This came up in a previous SO thread @ How to get the current task reference?
However, that thread was a bit more constrained by a particular interface.
The brute-force approach I threw together quickly just uses a dictionary of weak references.
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);
}
}
}
}
However, this clearly means whatever is creating the tasks is forced to deal with this additional headache, so it’s not very usable, especially WRT C#5 async methods where the task isn’t so explicitly created.
Again, probably a bad idea to have code that needs this, so consider it more akin to a thought exercise. 🙂
There’s not much of a better way, unfortunately, and since the eventual goal was to key off of Task.CurrentId, it’s indeed not even useful since we won’t have an ability to get ‘current task id’ for async methods (after the first await, so it’s returned to the caller already).