Il TaskCompletionSource<T>.Task
è costruito senza alcuna azione e lo scheduler viene assegnato alla prima chiamata a ContinueWith(...)
(da Asynchronous Programming with the Reactive Framework and the Task Parallel Library — Part 3).
Fortunatamente è possibile personalizzare il comportamento attendono leggermente implementando la propria classe derivanti da INotifyCompletion
e poi utilizzarlo in un modello simile a await SomeTask.ConfigureAwait(false)
per configurare il programma di pianificazione che l'attività deve iniziare ad usare nel metodo OnCompleted(Action continuation)
(da await anything;).
Ecco l'utilizzo:
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; }
Ecco una semplice implementazione di ConfigureScheduler
utilizzando un metodo di estensione operativa con la parte importante 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() { }
}
}
Ecco un esempio di lavoro che compilare come un'applicazione console:
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);
}
}
}
fonte
2013-06-21 20:19:29
Non è previsto attendere un task caldo (alias già in esecuzione)? Anche i task da TaskCompletionSource non possono essere "eseguiti", in quanto uno è responsabile del completamento dell'attività chiamando SetResult. Vuoi dire come specificare lo scheduler su cui viene eseguita la prosecuzione dall'attesa? – sanosdole
l'attività è calda e in esecuzione, mentre restituita dal CTS. Ma alcune programmazioni avvengono tra "SetResult" e la continuazione dopo l'attesa. Devo controllare quella pianificazione con il mio programmatore, se possibile –
@StephaneDelcroix Non puoi eseguire l'intero metodo sul tuo programma di pianificazione? Penso che sarebbe la soluzione migliore. – svick