1

从 .NET 4.0 开始,就有了执行异步任务的 TPL。如果您正在阅读 msdn,所有与表单/UI 交互的异步操作仍然使用 InvokeRequire ... Invoke() 模式。我要问的是这有原因吗?据我所知,TPL 应该是旧线程机制的替代品。那么在涉及 UI 线程时忽略它有什么意义呢?对此有什么想法吗?

4

4 回答 4

4

这似乎相当主观......

当您说“自 .NET 4.0 以来”时,您是在说“截至今年 4 月”-.net 已经存在 10 年了,并且 InvokeRequired/Invoke 已用于过去 9 年。为什么 MS 会破坏所有现有的出于任何原因的 UI 代码?即使存在调用线程的新方法,他们也不能简单地修改模式而没有巨大的兼容性问题。

此外,TPL 与 InvokeRequired/Invoke 不同——TPL 是关于简单的并行性,invoke 是关于在特定线程上运行代码。我不确定即使没有兼容性问题,为什么要替换另一个。

请注意,没有什么可以阻止您使用 TPL 来确保您在正确的线程上调用 UI 组件。事实上,您可以轻松做到这一点。但这取决于您,当前的 API 不会以不向后兼容的方式发生变化。

于 2010-09-22T12:10:32.913 回答
1

使用 TPL,您可以使用TaskScheduler.FromCurrentSynchronizationContext指定目标线程,此方法指定 Task 将在主线程上执行。我建议使用它而不是 Invoke。

于 2010-09-22T12:21:24.590 回答
1

这里的问题是什么?TPL 的存在并没有改变 UI 本质上是单线程的事实,并且要求控件只能在 UI 线程上访问。(这是 Windows 的限制,而不是 .NET UI 框架的限制。TPL 无法改变数十年的 Windows 设计限制。)

如果您的问题是关于将任务与 InvokeRequired/Invoke 混合使用,则有一种比 Invoke 更面向 TPL 的方式。TPL 提供了一种内置方式来安排延续任务在 UI 线程上运行。因此,您将面向背景的工作放在一个任务中,然后您的 UI 更新在另一个任务中。请参阅有关任务调度程序和 SynchronizationContext 的这篇文章

(但实际上,TPL 并没有取代任何旧的线程 API。如果 Invoke 是完成您想做的事情的最佳方式,请使用它。)

于 2010-09-22T12:22:01.167 回答
0
private void WorkProcessingAsync(IWorkItem workItem)
{
    IsBusy = true;
    /* =============================
    *  Create a TPL Task and pass the current UiCulture in an state Object to resolve the correct .resx file for translation / globalisation / Multilanguate features in Background Thread
    * ==============================*/
    Task<IWorkItem> task = Task.Factory.StartNew((stateObj) =>
    {
        // here we are already in the task background thread
        // save cast the given stateObj
        var tuple = stateObj as Tuple<IWorkItem, CultureInfo>;

        Debug.Assert(tuple != null, "tuple != null");

        Thread.CurrentThread.CurrentUICulture = tuple.Item2;   // Here we set the UI-Thread Culture to the Background Thread

        var longRunningOperationAnswer = LongRunningOperation.DoLongWork(tuple.Item1);
        return longRunningOperationAnswer;

    }, new Tuple<IWorkItem, CultureInfo>(workItem, Thread.CurrentThread.CurrentUICulture));  // here we pass the UI-Thread Culture to the State Object



    /* =======================================================================
    *   Handle OnlyOnRanToCompletion Task and process longRunningOperationAnswer back in UiThread
    * =======================================================================*/
    task.ContinueWith((t) =>
    {
        IsBusy = false;
        // handle longRunningOperationAnswer here in t.Result
        Log.Debug("Operation completet with {0}", t.Result);

    }, CancellationToken.None
    , TaskContinuationOptions.OnlyOnRanToCompletion
    , TaskScheduler.FromCurrentSynchronizationContext());

    /* =======================================================================
    *   Handle OnlyOnFaulted Task back in UiThread
    * =======================================================================*/
    task.ContinueWith((t) =>
    {
        IsBusy = false;
        AggregateException aggEx = t.Exception;

        if (aggEx != null)
        {
            aggEx.Flatten();
            Log.ErrorFormat("The Task exited with Exception(s) \n{0}", aggEx);
            foreach (Exception ex in aggEx.InnerExceptions)
            {
                if (ex is SpecialExaption)
                {
                    //Handle Ex here
                    return;
                }
                if (ex is CustomExeption)
                {
                    //Handle Ex here
                    return;
                }
            }
        }
    }, CancellationToken.None
    , TaskContinuationOptions.OnlyOnFaulted
    , TaskScheduler.FromCurrentSynchronizationContext());
}
于 2012-01-27T09:18:52.460 回答