The TaskCompletionSource<T>.Task
wird ohne eine Aktion ausgeführt und die Ablaufsteuerung ist auf dem ersten Aufruf von ContinueWith(...)
(von Asynchronous Programming with the Reactive Framework and the Task Parallel Library — Part 3) zugeordnet.
Zum Glück können Sie das await Verhalten leicht anpassen, indem Sie Ihre eigene Klasse ZUSAMMENHANG INotifyCompletion
Implementierung und Verwendung sie dann in einem Muster ähnlich await SomeTask.ConfigureAwait(false)
den Scheduler zu konfigurieren, dass die Aufgabe sollte mit der OnCompleted(Action continuation)
Methode (von await anything;) starten.Hier
ist die Nutzung:
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
public async Task Foo() {
// Force await to schedule the task on the supplied scheduler
await SomeAsyncTask().ConfigureScheduler(scheduler);
}
public Task SomeAsyncTask() { return source.Task; }
Hier ist eine einfache Implementierung von ConfigureScheduler
eine Task-Extension-Methode mit dem wichtigen Teil unter Verwendung von in OnCompleted
:
public static class TaskExtension {
public static CustomTaskAwaitable ConfigureScheduler(this Task task, TaskScheduler scheduler) {
return new CustomTaskAwaitable(task, scheduler);
}
}
public struct CustomTaskAwaitable {
CustomTaskAwaiter awaitable;
public CustomTaskAwaitable(Task task, TaskScheduler scheduler) {
awaitable = new CustomTaskAwaiter(task, scheduler);
}
public CustomTaskAwaiter GetAwaiter() { return awaitable; }
public struct CustomTaskAwaiter : INotifyCompletion {
Task task;
TaskScheduler scheduler;
public CustomTaskAwaiter(Task task, TaskScheduler scheduler) {
this.task = task;
this.scheduler = scheduler;
}
public void OnCompleted(Action continuation) {
// ContinueWith sets the scheduler to use for the continuation action
task.ContinueWith(x => continuation(), scheduler);
}
public bool IsCompleted { get { return task.IsCompleted; } }
public void GetResult() { }
}
}
Hier ist eine Arbeitsprobe, die wie kompilieren eine Konsolenanwendung:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Example {
class Program {
static TaskCompletionSource<object> source = new TaskCompletionSource<object>();
static TaskScheduler scheduler = new CustomTaskScheduler();
static void Main(string[] args) {
Console.WriteLine("Main Started");
var task = Foo();
Console.WriteLine("Main Continue ");
// Continue Foo() using CustomTaskScheduler
source.SetResult(null);
Console.WriteLine("Main Finished");
}
public static async Task Foo() {
Console.WriteLine("Foo Started");
// Force await to schedule the task on the supplied scheduler
await SomeAsyncTask().ConfigureScheduler(scheduler);
Console.WriteLine("Foo Finished");
}
public static Task SomeAsyncTask() { return source.Task; }
}
public struct CustomTaskAwaitable {
CustomTaskAwaiter awaitable;
public CustomTaskAwaitable(Task task, TaskScheduler scheduler) {
awaitable = new CustomTaskAwaiter(task, scheduler);
}
public CustomTaskAwaiter GetAwaiter() { return awaitable; }
public struct CustomTaskAwaiter : INotifyCompletion {
Task task;
TaskScheduler scheduler;
public CustomTaskAwaiter(Task task, TaskScheduler scheduler) {
this.task = task;
this.scheduler = scheduler;
}
public void OnCompleted(Action continuation) {
// ContinueWith sets the scheduler to use for the continuation action
task.ContinueWith(x => continuation(), scheduler);
}
public bool IsCompleted { get { return task.IsCompleted; } }
public void GetResult() { }
}
}
public static class TaskExtension {
public static CustomTaskAwaitable ConfigureScheduler(this Task task, TaskScheduler scheduler) {
return new CustomTaskAwaitable(task, scheduler);
}
}
public class CustomTaskScheduler : TaskScheduler {
protected override IEnumerable<Task> GetScheduledTasks() { yield break; }
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; }
protected override void QueueTask(Task task) {
TryExecuteTask(task);
}
}
}
Warten Sie nicht auf eine heiße (aka bereits laufende) Aufgabe? Außerdem können Tasks von TaskCompletionSource nicht "ausgeführt" werden, da Sie für das Abschließen der Task verantwortlich sind, indem Sie SetResult aufrufen. Meinst du, wie man den Scheduler spezifiziert, auf dem die Fortsetzung von Warten ausgeführt wird? – sanosdole
die Aufgabe ist heiß und läuft, während von der CTS zurückgegeben. Es gibt jedoch eine zeitliche Planung zwischen dem SetResult und der Fortsetzung nach dem Warten. Ich muss diese Planung mit meinem eigenen Scheduler steuern, wenn möglich –
@StephaneDelcroix Können Sie nicht die gesamte Methode auf Ihrem Scheduler ausführen? Ich denke, das wäre die beste Lösung. – svick