TPL vs. InvokeRequired/Invoke

2019-08-02 06:34发布

问题:

Since .NET 4.0 there is the TPL to execute asynchronous tasks. If you are reading the msdn, all async operations interacting with forms / UI still uses the InvokeRequire ... Invoke() pattern. What I'm asking is that is there a reason for it? From what I learned, TPL should be sort of a replacement of older threading mechanisms. So what's the point to ignore it when it's about UI threading? Any thoughts about that?

回答1:

This seems fairly subjective...

When you say "Since .NET 4.0", you are saying "as of April of this year" - .net has been around for 10 years now, and InvokeRequired/Invoke has been used for the last 9. Why would MS break all existing UI code for any reason? Even if a new way of calling to a thread existed, they could not simply modify the pattern without huge compatibility concerns.

Also, the TPL is not analogous to InvokeRequired/Invoke - the TPL is about easy parallelism, and invoke is about running code on a specific thread. I'm not sure why one would replace the other even if there were no compatibility concerns.

Note that there is nothing stopping you from using the TPL to ensure you call UI components on the correct thread. In fact, you can easily do this. But that is up to you and the current API is not going to change in a way that isn't backwards compatible.



回答2:

With TPL you can specify target thread using TaskScheduler.FromCurrentSynchronizationContext this method specify that Task will be executed on main thread. I recommend using it instead of Invoke.



回答3:

What is the question here? The existence of TPL doesn't change the fact that UIs are inherently single-threaded, and require controls to be accessed only on the UI thread. (And that's a Windows limitation, not a limitation of the .NET UI frameworks. TPL can't change decades of Windows design limitations.)

If your question is about mixing Tasks with InvokeRequired/Invoke, there is a more TPL-oriented way than Invoke. TPL comes with a built-in way to schedule a continuation Task to run on the UI thread. So you put your background-oriented work in one Task, and then your UI updates in another Task. See this post on task schedulers and SynchronizationContext.

(But really, TPL doesn't replace any of the older thread APIs. If Invoke is the best way to do what you're trying to do, use it.)



回答4:

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