简体   繁体   English

WinForm从控制台应用程序调用异步更新UI状态

[英]WinForm asynchronously update UI status from console application call

I want to asynchronously update UI status when doing a long-time task . 我想在执行长期任务时异步更新UI状态。 The program is a console application , however , when I execute the async operations , the UI thread will exit soon after the task begins . 该程序是一个控制台应用程序,但是,当我执行异步操作时,UI线程将在任务开始后立即退出。 How should I let the UI thread wait when my long-time task finish ? 我的长期任务完成后,应该如何让UI线程等待?

I simplify my code as below : 我将代码简化如下:

public static class Program
{
    static void Main()
    {
        WorkerWrapper wp = new WorkerWrapper();
        wp.ProcessData();
    }
}

public class WorkerWrapper
{
    private RateBar bar;

    public void ProcessData()
    {
        bar = new RateBar();
        bar.Show();

        Worker wk = new Worker();
        wk.WorkProcess += wk_WorkProcess;

        Action handler = new Action(wk.DoWork);
        var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
    }

    private void AsyncCallback(IAsyncResult ar)
    {
        Action handler = ar.AsyncState as Action;
        handler.EndInvoke(ar);
    }

    private void wk_WorkProcess(object sender, PrecentArgs e)
    {
        if (e.Precent < 100)
        {
            bar.Precent = e.Precent;
        }
    }
}

public class Worker
{
    public event EventHandler<PrecentArgs> WorkProcess;
    public void DoWork()
    {
        for (int i = 0; i < 100; i++)
        {
            WorkProcess(this, new PrecentArgs(i));
            Thread.Sleep(100);
        }
    }
}

public class PrecentArgs : EventArgs
{
    public int Precent { get; set; }
    public PrecentArgs(int precent)
    {
        Precent = precent;
    }
}

public partial class RateBar : Form
{
    public int Precent
    {
        set
        {
            System.Windows.Forms.MethodInvoker invoker = () => this.progressBar1.Value = value;
            if (this.progressBar1.InvokeRequired)
            {
                this.progressBar1.Invoke(invoker);
            }
            else
            {
                invoker();
            }
        }
    }

    public RateBar()
    {
        InitializeComponent();
    }
}

However , in method ProcessData() , if I add result.AsyncWaitHandle.WaitOne() in the end to wait my operation to complete , the Form will freeze . 但是,在方法ProcessData()中,如果最后添加result.AsyncWaitHandle.WaitOne()等待操作完成,则Form将冻结。

Is there anything wrong with my way to wait the thread to complete ? 我等待线程完成的方式有什么问题吗?

Reason that your application exiting before your "background threads" completed is when there are multiple threads application exists soon after there are not any foreground threads. 您的应用程序在“后台线程”完成之前退出的原因是当有多个线程时,应用程序在没有任何前台线程之后不久就存在。 This is explained more in here http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx 有关更多信息,请参见http://msdn.microsoft.com/zh-cn/library/system.threading.thread.isbackground(v=vs.110).aspx

You should add proper waiting mechanisms to your background threads to be completed. 您应该在后台线程中添加适当的等待机制以完成操作。 There are multiple ways of letting other threads know that the thread is complete. 有多种让其他线程知道该线程已完成的方法。 Please refer here. 请在这里参考。 How to wait for thread to finish with .NET? NET中如何等待线程完成?

You shouldn't block the UI thread waiting for the result, but rather retrieve the result from EndInvoke . 您不应阻塞UI线程等待结果,而应从EndInvoke检索结果。 Your deadlock probably occurs because you are using both result.AsyncWaitHandle.WaitOne() and EndInvoke , both will block until the result is available. 由于同时使用了result.AsyncWaitHandle.WaitOne()EndInvoke ,因此可能会死锁,直到结果可用为止。

In my opinion the best option is to not call result.AsyncWaitHandle.WaitOne() and just retrieve the result in the AsyncCallback 我认为最好的选择是不调用result.AsyncWaitHandle.WaitOne()并仅在AsyncCallback检索结果

private void AsyncCallback(IAsyncResult ar)
{
    Action handler = ar.AsyncState as Action;
    var result = handler.EndInvoke(ar);               
}

More information here . 更多信息在这里 Also if you are using .net 4.0 or higher, this sort of thing can be done much easier with async/await . 同样,如果您使用的是.net 4.0或更高版本,则可以使用async/await轻松完成这种事情。

I write down this solution and hope it may helps others with same question . 我写下这个解决方案,希望对其他有相同问题的人有所帮助。 The key to this problem is to use a new thread to run RateBar's ShowDialog function . 解决此问题的关键是使用新线程运行RateBar的ShowDialog函数。

 public void ProcessData()
 {
     new Thread(() => new RateBar().ShowDialog()).Start(); 

     Worker wk = new Worker();
     wk.WorkProcess += wk_WorkProcess;

     Action handler = new Action(wk.DoWork);
     var result = handler.BeginInvoke(new AsyncCallback(this.AsyncCallback), handler);
 }

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

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