TPL与InvokeRequired / Invoke

时间:2010-09-22 11:59:40

标签: .net task multithreading task-parallel-library

从.NET 4.0开始,有TPL来执行异步任务。如果您正在阅读msdn,则与窗体/ UI交互的所有异步操作仍使用InvokeRequire ... Invoke()模式。 我问的是,有什么理由吗?根据我的经验,TPL应该是旧的线程机制的替代品。那么当关于UI线程时忽略它有什么意义呢? 有什么想法吗?

4 个答案:

答案 0 :(得分:4)

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

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

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

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

如果你的问题是关于将Tasks与InvokeRequired / Invoke混合,那么有一种比Invoke更面向TPL的方式。 TPL提供了一种内置方式来安排在UI线程上运行的继续任务。因此,您将面向后台的工作放在一个任务中,然后在另一个任务中更新UI。请参阅this post on task schedulers and SynchronizationContext

(但实际上,TPL不会替换任何旧的线程API。如果Invoke是您尝试做的最佳方式,请使用它。)

答案 3 :(得分: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());
}