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

时间:2014-05-27 06:44:43

标签: c# multithreading winforms asynchronous begininvoke

我想在执行长时间任务时异步更新UI状态。该程序是一个控制台应用程序,但是,当我执行异步操作时,UI线程将在任务开始后立即退出。 当我的长期任务结束时,我应该如何让UI线程等待?

我简化了我的代码,如下所示:

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

但是,在方法ProcessData()中,如果我在最后添加result.AsyncWaitHandle.WaitOne()以等待我的操作完成,则表单将冻结。

等待线程完成的方式有什么问题吗?

3 个答案:

答案 0 :(得分:0)

您的应用程序在&#34;后台线程&#34;之前退出的原因完成是在没有任何前台线程后很快存在多个线程的应用程序。这里将更详细地解释http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx

您应该为后台线程添加正确的等待机制来完成。有多种方法可以让其他线程知道线程是完整的。请参考这里。 How to wait for thread to finish with .NET?

答案 1 :(得分:0)

您不应该阻止等待结果的UI线程,而是从EndInvoke检索结果。您的死锁可能因为您同时使用result.AsyncWaitHandle.WaitOne()EndInvoke而发生,两者都会阻塞,直到结果可用。

在我看来,最好的选择是不要拨打result.AsyncWaitHandle.WaitOne()并只检索AsyncCallback

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

更多信息here。此外,如果您使用.net 4.0或更高版本,使用async/await可以更轻松地完成此类操作。

答案 2 :(得分:0)

我写下了这个解决方案,并希望它可以帮助其他人提出同样的问题。 这个问题的关键是使用一个新线程来运行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);
 }