アプリケーションで実行されている多くのタスクを使用します。タスクの各束は、何らかの理由で実行されています。これらのタスクに名前を付けたいので、Parallel Tasksウィンドウを見ると、簡単に認識できます。
別の観点から、フレームワークレベルのタスクを使用してリストにデータを入力していると考えてください。私のフレームワークを使用する開発者も、自分の仕事にタスクを使用しています。彼女がParallelTasksウィンドウを見ると、何も知らないタスクがいくつか見つかります。彼女がフレームワークタスクと自分のタスクを区別できるように、タスクに名前を付けたいと思います。
このような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;
}
}
Task
に実際に名前を付けることはできませんが、Task
によって実行されるメソッドに名前を付けることはできます。これは、[並列タスク]ウィンドウに表示されます。したがって、Task
sに名前を付けることが重要な場合は、ラムダを使用せず、通常の名前付きメソッドを使用してください。
驚いたことに、これはParallel
でも機能しますが、Task
はメソッドを直接実行していません。これは、ParallelTasksがTask
からParallel
sを何らかの形で認識し、それらの処理が異なるためだと思います。
任意のオブジェクトを任意のオブジェクトに関連付けることができます。これがタスクの拡張機能です。 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;
}
}
タスクに名前を付けることはできません。
タスクライブラリは内部でスレッドプールを使用しているため、スレッドに名前を付けることはできません。また、「。ContinueWith()」のようなメソッドは常に新しいタスクを作成し、クラスから継承しないため、継承アプローチは機能しません。
タスクの終了後にタスクの名前だけを知る必要がある場合は、それをパラメーターとして渡すことができます。タスク結果の一部として返します。
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}");
}
タスクに名前を付けることができるとは思いません。 Task.Id
を使用してタスクを追跡できます。
Parallel Tasksウィンドウの動作がわからないため、ここではブラインドで撮影していますが、デバッガーAPIを使用している場合は、NamedTaskサブクラスに DebuggerDisplay 属性を追加すると役立つ場合があります。
おかげで マイクの答え 私は結局:
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;
}
}
現在はスレッドセーフであり、タグだけでなく名前もサポートしています。
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....");
}
}
});
}
}
デバッグなどに役立つ辞書を考えました。
これが私がやってきたことのサンプルです:
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());
}