TPL 与 InvokeRequired/Invoke

发布于 2024-09-24 14:51:39 字数 174 浏览 2 评论 0原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鱼窥荷 2024-10-01 14:51:39

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

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

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

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

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.

苍暮颜 2024-10-01 14:51:39

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

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.

落花随流水 2024-10-01 14:51:39

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

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

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

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.)

彻夜缠绵 2024-10-01 14:51:39
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());
}
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());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文