如何获取当前任务参考?

如何引用我的代码在其中执行的任务?

ISomeInterface impl = new SomeImplementation(); Task.Factory.StartNew(() => impl.MethodFromSomeInterface(), new MyState()); ... void MethodFromSomeInterface() { Task currentTask = Task.GetCurrentTask(); // No such method? MyState state = (MyState) currentTask.AsyncState(); } 

因为我正在调用一些接口方法,所以我不能只将新创建的任务作为附加参数传递。

由于您无法更改界面或实现,因此您必须自己完成,例如,使用ThreadStaticAttribute

 static class SomeInterfaceTask { [ThreadStatic] static Task Current { get; set; } } ... ISomeInterface impl = new SomeImplementation(); Task task = null; task = Task.Factory.StartNew(() => { SomeInterfaceTask.Current = task; impl.MethodFromSomeInterface(); }, new MyState()); ... void MethodFromSomeInterface() { Task currentTask = SomeInterfaceTask.Current; MyState state = (MyState) currentTask.AsyncState(); } 

这是一个可以用于它的“hacky”类。
只需使用CurrentTask属性即可获取当前正在运行的Task。
我强烈建议不要在生产代码附近的任何地方使用它!

 public static class TaskGetter { private static string _propertyName; private static Type _taskType; private static PropertyInfo _property; private static Func _getter; static TaskGetter() { _taskType = typeof(Task); _propertyName = "InternalCurrent"; SetupGetter(); } public static void SetPropertyName(string newName) { _propertyName = newName; SetupGetter(); } public static Task CurrentTask { get { return _getter(); } } private static void SetupGetter() { _getter = () => null; _property = _taskType.GetProperties(BindingFlags.Static | BindingFlags.NonPublic).Where(p => p.Name == _propertyName).FirstOrDefault(); if (_property != null) { _getter = () => { var val = _property.GetValue(null); return val == null ? null : (Task)val; }; } } } 

以下示例显示了如何实现它,并通过@ stephen-cleary提供的答案解决问题。 它有点复杂,但实际上关键是在下面的TaskContext类中,它使用CallContext.LogicalSetDataCallContext.LogicalGetDataCallContext.FreeNamedDataSlot ,它们对创建自己的Task上下文很有用。 剩下的就是回答OP的问题:

 class Program { static void Main(string[] args) { var t1 = Task.Factory.StartNewWithContext(async () => { await DoSomething(); }); var t2 = Task.Factory.StartNewWithContext(async () => { await DoSomething(); }); Task.WaitAll(t1, t2); } private static async Task DoSomething() { var id1 = TaskContext.Current.Task.Id; Console.WriteLine(id1); await Task.Delay(1000); var id2 = TaskContext.Current.Task.Id; Console.WriteLine(id2); Console.WriteLine(id1 == id2); } } public static class TaskFactoryExtensions { public static Task StartNewWithContext(this TaskFactory factory, Action action) { Task task = null; task = new Task(() => { Debug.Assert(TaskContext.Current == null); TaskContext.Current = new TaskContext(task); try { action(); } finally { TaskContext.Current = null; } }); task.Start(); return task; } public static Task StartNewWithContext(this TaskFactory factory, Func action) { Task task = null; task = new Task(async () => { Debug.Assert(TaskContext.Current == null); TaskContext.Current = new TaskContext(task); try { await action(); } finally { TaskContext.Current = null; } }); task.Start(); return task.Unwrap(); } } public sealed class TaskContext { // Use your own unique key for better performance private static readonly string contextKey = Guid.NewGuid().ToString(); public TaskContext(Task task) { this.Task = task; } public Task Task { get; private set; } public static TaskContext Current { get { return (TaskContext)CallContext.LogicalGetData(contextKey); } internal set { if (value == null) { CallContext.FreeNamedDataSlot(contextKey); } else { CallContext.LogicalSetData(contextKey, value); } } } } 

如果您可以使用.NET 4.6或更高版本,.NET Standard或.NET Core,他们已经使用AsyncLocal解决了这个问题。 https://docs.microsoft.com/en-gb/dotnet/api/system.threading.asynclocal-1?view=netframework-4.7.1

如果没有,您需要在使用之前设置一个数据存储,并通过闭包而不是线程或任务访问它。 ConcurrentDictionary将帮助掩盖您执行此操作时所犯的任何错误。

当代码等待时,当前任务释放线程 – 即线程与任务无关,至少在编程模型中。

演示:

 // I feel like demo code about threading needs to guarantee // it actually has some in the first place :) // The second number is IOCompletionPorts which would be relevant // if we were using IO (strangely enough). var threads = Environment.ProcessorCount * 4; ThreadPool.SetMaxThreads(threads, threads); ThreadPool.SetMinThreads(threads, threads); var rand = new Random(DateTime.Now.Millisecond); var tasks = Enumerable.Range(0, 50) .Select(_ => { // State store tied to task by being created in the same closure. var taskState = new ConcurrentDictionary(); // There is absolutely no need for this to be a thread-safe // data structure in this instance but given the copy-pasta, // I thought I'd save people some trouble. return Task.Run(async () => { taskState["ThreadId"] = Thread.CurrentThread.ManagedThreadId; await Task.Delay(rand.Next() % 100); return Thread.CurrentThread.ManagedThreadId == (int)taskState["ThreadId"]; }); }) .ToArray(); Task.WaitAll(tasks); Console.WriteLine("Tasks that stayed on the same thread: " + tasks.Count(t => t.Result)); Console.WriteLine("Tasks that didn't stay on the same thread: " + tasks.Count(t => !t.Result));