2014-02-20 59 views
7

我手動構建一個任務:Task.Start,Task.RunSynchronously和同步上下文

var task = new Task(() => 
    Debug.WriteLine("Task")); 

然後手動啓動它:

task.Start(TaskScheduler.FromCurrentSynchronizationContext()); 

我希望它是通過SynchronizationContext.Post安排。

但是,如果啓動它是這樣的:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext()); 

它會通過SynchronizationContext.Send執行,或者直接通過調用任務的lambda?

回答

8

它會通過SynchronizationContext.Send執行,還是直接通過 調用任務的lambda?

這是發生了什麼事。首先,Task.RunSynchronously嘗試通過呼叫scheduler.TryExecuteTaskInline來執行任務。如果有SynchronizationContextTaskScheduler,那就是:

protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 
{ 
    return ((SynchronizationContext.Current == this.m_synchronizationContext) && base.TryExecuteTask(task)); 
} 

所以,如果在同一同步上下文,任務拉姆達會直接內base.TryExecuteTask執行。

否則,Task.RunSynchronously將任務與任務調度程序排隊,並使用阻塞等待阻塞任務的WaitHandle

SynchronizationContext.Send確實不是涉及任何情況。

有趣的是這個。 AFAIK,在WPF中,主UI線程上可能會有多個DispatcherSynchronizationContext上下文,每個頂級窗口上有一個上下文。因此,理論上,RunSynchronously可能導致死鎖。我要驗證這一點。

更新,WPF中的死鎖是real。這裏是如何重載:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 

     this.Loaded += MainWindow_Loaded; 
    } 

    void MainWindow_Loaded(object sMainWindow, RoutedEventArgs eMainWindow) 
    { 
     var task = new Task(() => 
      Debug.WriteLine("Task")); 

     var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

     var window1 = new Window(); 
     window1.Loaded += (sWindow1, eWindow1) => 
     { 
      // Deadlock in WPF 
      task.RunSynchronously(scheduler); 
      Debug.WriteLine("Done!"); 
     }; 
     window1.Show(); 
    } 
} 
+0

我很難想象死鎖,在這裏。你介意描述這如何導致僵局?即誰在爭奪什麼資源? – Thomas

+1

@Thomas,沒有比賽,但任務完成回調通過SynchrnizationContext.Post發佈,然後線程阻塞task.RunSynchronously內同一任務的句柄,導致死鎖。我正在通過手機進行此操作,不能進入更多細節,但是您可以搜索Stephen Clearly的「不要阻止異步代碼」。 – Noseratio