简体   繁体   English

在UI线程中的任务完成时显示表单

[英]Show form at task completion in UI thread

I have a task which, when completed, is supposed to continue with another task that shows a winform (the winform was previously initialised on the UI thread, so it does have a handle). 我有一个任务,完成后应该继续执行另一个显示Winform的任务(该Winform先前已在UI线程上初始化,因此它确实具有句柄)。

    private static Task RunningTask
    {
        get;
        set;
    }
    public static UpdaterTool.Forms.UpdateResult UpdateResultForm;

    private void DoWork()
    {
        UpdateResultForm = new Forms.UpdateResult(); 
        //the next line forces the creation of the handle - 
        //otherwise InvokeRequired will later on return false.
        var hackHandle = UpdateResultForm.Handle; 

        var ctx = TaskScheduler.FromCurrentSynchronizationContext();

        RunningTask = Task.Factory.StartNew(() => DownloadAndInstallFiles(), CancelTokenSource.Token)
            .ContinueWith(_ => WorkComplete(), CancelTokenSource.Token, TaskContinuationOptions.NotOnFaulted, ctx);
    }

    private void WorkComplete()
    {
       ShowResultForm();
    }

    private void ShowResultForm()
    {
        if (UpdateResultForm.InvokeRequired)
        {
            try
            {
                UpdateResultForm.Invoke(new MethodInvoker(ShowResultForm));
            }
            catch { }
            return;
         }
         UpdateResultForm.Show();

     }

The problem is that no matter what combination of overloads for the ContinueWith() I use, the UpdateResultForm is either not shown at all(meaning the continuation does not happen, the worker hangs at "running"), or when it is, it hangs the UI, like its expecting the worker thread to finish or something. 问题是,无论我使用的ContinueWith()的重载是什么组合,UpdateResultForm要么根本不显示(这意味着连续性不会发生,工作程序会在“运行中”挂起),要么在什么时候挂起UI,就像它期望工作线程完成一样。 I dont understand why this happens when I tried to show it on the UI thread, by using FromCurrentSynchronizationContext(). 我不明白为什么尝试使用FromCurrentSynchronizationContext()在UI线程上显示它时发生这种情况。

In my understanding, inside the DoWork method I start on the UI thread (which is why I initialise the form there). 以我的理解,在DoWork方法中,我从UI线程开始(这就是为什么在那里初始化表单的原因)。 When the code enters Task.Factory.StartNew, it switches to the working thread. 当代码输入Task.Factory.StartNew时,它将切换到工作线程。 When it completes, it continues with WorkComplete which just shows the previously initialised form, on the UI thread. 完成后,将继续执行WorkComplete,该操作仅在UI线程上显示先前初始化的表单。

What am I missing? 我想念什么? Thanks, 谢谢,

Using InvokeRequired is a strong anti-pattern. 使用InvokeRequired是一个强大的反模式。 It's not that common to have no idea on what thread a method runs. 不知道方法在哪个线程上运行并不常见。 That's the case here as well, you already used TaskScheduler.FromCurrentSynchronizationContext() so you know that the task runs on the UI thread. 在这里也是如此,您已经使用TaskScheduler.FromCurrentSynchronizationContext(),因此您知道任务在UI线程上运行。 There's no point in checking again. 再次检查毫无意义。 Which also eliminates the need to create the form handle early. 这也消除了早期创建表单句柄的需要。

The kind of problems you are having can be caused by running DoWork() from a worker thread. 您遇到的这类问题可能是通过从工作线程运行DoWork()引起的。 Maybe you called it from a task as well. 也许您也从任务中调用了它。 Which makes FromCurrentSynchronizationContext() return the wrong context. 这使得FromCurrentSynchronizationContext()返回错误的上下文。 And will display the form on a thread that doesn't pump a message loop, it will be dead as a doornail. 并且将在不泵送消息循环的线程上显示表单,该表单将作为门钉而失效。 Or by blocking the UI thread, waiting for the task to finish. 或通过阻止UI线程,等待任务完成。 That will always cause deadlock, the task cannot complete unless the UI thread goes idle and can execute the ShowResultForm() method. 这将始终导致死锁,除非UI线程处于空闲状态并且可以执行ShowResultForm()方法,否则任务无法完成。

I think the solution to this is: Is DoWork() called on any Thread other than the UI Thread? 我认为解决方案是:是否在除UI线程之外的任何线程上调用DoWork()

If yes , then consider instantiating the UpdateResultForm somwhere on the UI Thread or if this is not possible then somwhere within your ContinueWith operation. 如果 ,则考虑在UI线程上实例化UpdateResultForm ,如果无法实现,则在ContinueWith操作中进行实例化。

If no then there shouldn't be a problem. 如果没有,那应该没有问题。 The UpdateResultForm.Handle could have caused some trouble. UpdateResultForm.Handle可能引起了一些麻烦。 However, this handle is no longer needed in this case, as you are on the UI Thread already and therefore don't have to check wether Invoke is required or not. 但是,在这种情况下,不再需要此句柄,因为您已经在UI Thread上,因此不必检查是否需要Invoke。

In any of both cases you could try rewriting your DoWork method like this: 在这两种情况下,您都可以尝试这样重写DoWork方法:

     private void DoWork()
            {
                var ctx = TaskScheduler.FromCurrentSynchronizationContext();

                RunningTask = Task.Factory
                    .StartNew(DownloadAndInstallFiles, CancelTokenSource.Token)
                    .ContinueWith(_ => ShowResultForm(), CancelTokenSource.Token, TaskContinuationOptions.NotOnFaulted, ctx);
            }

            private void ShowResultForm()
            {
                UpdateResultForm = new Forms.UpdateResult();
                // Some other code
                UpdateResultForm.Show();
            }

Even WorkComplete is no longer required as it merely passes the call thru. 甚至WorkComplete也不再需要,因为它只是通过调用。 Hope this will help you. 希望这会帮助你。

If you're using SynchronizationContext then you don't need the InvokeRequired/Invoke. 如果您使用的是SynchronizationContext,则不需要InvokeRequired / Invoke。 The only way you can run into the problem you're seeing here is if DoWork is invoked on a thread other than the UI thread. 遇到此问题的唯一方法是,是否在UI线程以外的其他线程上调用DoWork。

eg if I take the code you provided, create my own UpdateResult form, add a CancelTokenSource member and initialize it in the constructor, and run DoWork from the UI thread, it works fine. 例如,如果我采用您提供的代码,创建自己的UpdateResult表单,添加一个CancelTokenSource成员并在构造函数中对其进行初始化,然后从UI线程运行DoWork,则可以正常工作。

I solved it. 我解决了 The answers on this thread are all correct, the issue here was, just like Hans suggested, that FromCurrentSynchronizationContext() was returning a wrong context instead of the expected UI one. 这个线程的答案都是正确的,就像汉斯建议的那样,这里的问题是FromCurrentSynchronizationContext()返回了错误的上下文,而不是预期的UI。 This happened because DoWork was called from a worker thread, and not from the UI one. 发生这种情况是因为DoWork是从辅助线程而不是从UI调用的。

The solution here in my particular case, was to call DoWork from a UI context, which means I had to make a WindowsFormsSynchronizationContext object just like this blog post explains . 在我的特定情况下,这里的解决方案是从UI上下文调用DoWork,这意味着我必须制作一个WindowsFormsSynchronizationContext对象,就像本博文解释的那样

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM