【问题标题】:Task.Start, Task.RunSynchronously and synchronization contextTask.Start、Task.RunSynchronously 和同步上下文
【发布时间】:2014-02-20 13:37:05
【问题描述】:

我手动构建一个任务:

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

然后手动启动:

task.Start(TaskScheduler.FromCurrentSynchronizationContext());

我希望通过SynchronizationContext.Post 安排它。

但是如果以这种方式开始:

task.RunSynchronously(TaskScheduler.FromCurrentSynchronizationContext());

是通过SynchronizationContext.Send执行,还是直接调用任务的lambda?

【问题讨论】:

    标签: c# .net task-parallel-library task synchronizationcontext


    【解决方案1】:

    是通过 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));
    }
    

    因此,如果在同一个同步上下文中,任务 lambda 将直接在 base.TryExecuteTask 内执行。

    否则,Task.RunSynchronously 将任务与任务调度程序一起排队,并在任务的WaitHandle 上阻塞等待。

    SynchronizationContext.Send参与任何案件。

    这有什么有趣的。 AFAIK,在 WPF 中,主 UI 线程上可能有多个 DispatcherSynchronizationContext 上下文,每个顶级窗口一个。因此,理论上,RunSynchronously 可能会导致死锁。我要验证一下。

    更新,WPF中的死锁是真实的。以下是如何复制:

    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();
        }
    }
    

    【讨论】:

    • 我很难想象这里的僵局。你介意描述一下这是如何导致死锁的吗?即谁在争夺所持有的资源?
    • @Thomas,没有比赛,但是任务完成回调是通过 SynchrnizationContext.Post 发布的,然后线程阻塞在 task.RunSynchronously 内的同一任务的句柄上,导致死锁。我是通过电话来的,不能详细说明,但你可以搜索 Stephen Clearly 的“不要阻塞异步代码”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多