【问题标题】:WinForm asynchronously update UI status from console application callWinForm 从控制台应用程序调用异步更新 UI 状态
【发布时间】:2014-05-27 06:44:42
【问题描述】:

我想在执行长时间任务时异步更新 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() 以等待我的操作完成,表单将冻结。

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

【问题讨论】:

    标签: c# multithreading winforms asynchronous begininvoke


    【解决方案1】:

    您的应用程序在“后台线程”完成之前退出的原因是当有多个线程时,应用程序在没有任何前台线程之后很快就存在了。这在http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.110).aspx

    中有更多解释

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

    【讨论】:

    • 如果我使用result.AsyncWaitHandle.WaitOne() 等待线程完成,表单将冻结。有什么问题吗 ? @DSharper
    • 您是否尝试过通过设置 isBackground = false 使“DoWork”方法作为前台线程运行? @wlz
    【解决方案2】:

    您不应阻塞等待结果的 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 可以更轻松地完成此类操作。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-19
        • 1970-01-01
        • 1970-01-01
        • 2011-01-21
        • 2019-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多