2016-05-30 30 views
2

鑑於我已經擁有的SynchronizationContext(並且基本上是特定主題的窗口),我如何創建發佈到此上下文的Task如何創建發佈到給定SynchronizationContext的任務?

作爲參考,這裏是一個非常基本的示範如何設置SynchronizationContext

public class SomeDispatcher : SynchronizationContext 
{ 
    SomeDispatcher() { 

     new Thread(() => { 

      SynchronizationContext.SetSynchronizationContext(this); 

      // Dispatching loop (among other things) 

     }).Start(); 
    } 

    override void Post(SendOrPostCallback d, object state) 
    { 
     // Add (d, state) to a dispatch queue; 
    } 
} 

這對於異步/等待那些已經在上下文中運行工作正常。

現在,我想能夠從外部上下文(例如從UI線程)發佈Task s,但似乎無法找到乾淨的方式來做到這一點。

一種方法是使用TaskCompletionSource<>

Task StartTask(Action action) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    SaidDispatcher.Post(state => { 
     try 
     { 
      action.Invoke(); 
      tcs.SetResult(null); 
     } 
     catch (Exception ex) 
     { 
      tcs.SetException(ex); 
     } 
    }); 
    return tcs.Task; 
}); 

但這是重新發明輪子和一個重大的痛苦支持的變化,如StartNew(Func<TResult>)StartNew(Func<Task<TResult>>)

一個TaskFactory接口的SynchronizationContext可能是最好的,但我似乎無法實例化一個清潔:

TaskFactory CreateTaskFactory() 
{ 
    var original = SynchronizationContext.Current; 
    SynchronizationContext.SetSynchronizationContext(SomeDispatcher); // yuck! 
    try 
    { 
     return new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
    finally 
    { 
     SynchronizationContext.SetSynchronizationContext(original); 
    } 
} 

(即具有臨時軟管當前線程的同步上下文似乎哈克。)

+0

通過複製現有的SynchronizationContextTaskScheduler的邏輯來實現自定義的TaskScheduler怎麼樣?當然不需要複製,邏輯本身看起來很簡單。 – Evk

+0

@Evk有幾個'internal's你不能使用,但主要想法仍然存在 - 你需要你自己的'TaskScheduler'。 – Luaan

+1

爲什麼要將任務發佈到同步上下文?同步上下文的通常使用在同步上下文中開始。 –

回答

4

似乎默認SynchronizationContextTaskScheduler

  1. 內部
  2. 只適用於當前同步上下文

但它的源代碼可用here,我們看到它的比較簡單,所以我們可以嘗試推出我們自己的調度程序,像這樣:

public sealed class MySynchronizationContextTaskScheduler : TaskScheduler { 
    private readonly SynchronizationContext _synchronizationContext; 

    public MySynchronizationContextTaskScheduler(SynchronizationContext context) { 
     _synchronizationContext = context; 
    } 

    [SecurityCritical] 
    protected override void QueueTask(Task task) { 
     _synchronizationContext.Post(PostCallback, task); 
    } 

    [SecurityCritical] 
    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { 
     if (SynchronizationContext.Current == _synchronizationContext) { 
      return TryExecuteTask(task); 
     } 
     else 
      return false; 
    } 

    [SecurityCritical] 
    protected override IEnumerable<Task> GetScheduledTasks() { 
     return null; 
    } 

    public override Int32 MaximumConcurrencyLevel 
    { 
     get { return 1; } 
    } 

    private void PostCallback(object obj) { 
     Task task = (Task) obj; 
     base.TryExecuteTask(task); 
    } 
} 

然後你的CreateTaskFactory變爲:

TaskFactory CreateTaskFactory() { 
    return new TaskFactory(new MySynchronizationContextTaskScheduler(SomeDispatcher)); 
} 

你創建任務:

var factory = CreateTaskFactory(); 
var task = factory.StartNew(...); 
+0

你還可以添加一個如何使用這個的例子,考慮到如何添加任務,而不是如何編寫taskscheduler? – Default

+0

@默認添加。 – Evk

+0

這看起來不錯!我想知道他們爲什麼不提供'TaskScheduler.FromSynchronizationContext(SynchronizationContext)',如果這是因爲允許任何舊的同步上下文有一些設計問題,但我想不出任何。 (編輯:刪除噪聲) – antak

2

Parallel Extensions Extras contains SynchronizationContextTaskScheduler這不正是你想要的東西。

如果您不想自己編譯PEE,請撥打there is an unofficial NuGet package for it

請注意,您通常不需要這樣做,而且您要求提供這一點的事實可能表明您的設計存在缺陷。

+0

很高興知道這個用例至少是間接支持的。 *你通常不需要這樣做*:假設我有一個高度定製的任務分派器(一個線程,其任務調用循環在其核心),我想(可追溯)添加* async */* await *支持。自然進程似乎是實現'SynchronizationContext' - >'TaskScheduler' - >'TaskFactory'。有沒有更好的辦法? (或者你是否說需要創建一個可能是問題的自定義調度程序?) – antak

+0

@antak爲什麼你甚至需要'SynchronizationContext'?自定義的'TaskScheduler'不夠嗎? – svick

+0

我很激動,因爲我認爲我可以將TaskScheduler.Current而不是'syncctx.Current'設置爲[在同一線程中恢復*等待* s](https://msdn.microsoft.com/雜誌/ gg598924.aspx)。但是,'TaskScheduler.Current' [看起來是完全不同的](http://stackoverflow.com/a/23072503/1036728),並不是我可以設置的某個線程本地。如果調度員所做的一切都是通過'TaskScheduler'完成的,那麼這可能不是問題,但是當它調用Task之外的例程,然後* await *開始中斷。有什麼我失蹤? – antak