【问题标题】:How to wait for thread to complete without blocking UI如何在不阻塞 UI 的情况下等待线程完成
【发布时间】:2015-09-18 13:46:03
【问题描述】:

我希望我的程序在下面一行之后等待

frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);

如上方法是通过 StartProcessWithProgress() 方法在内部调用线程。我希望在执行 //code logic -2 行之前完成该线程。同时,它不应该停止由 frmProgressBar.UpdateProgress() 完成的 UI 更新。我该怎么做呢?

namespace NS1
{
    public partial class frmMain : Form
    {                
        private void button1_Click(object sender, EventArgs e)
        {
            frmProgressBar frmProgressBarObj = PullMSI.ExtractByMSIName("products.txt", false);
            //code logic - 2
            MessageBox.Show("This is executing immediately. 
                             I want to wait until above thread is complete");
        }
    }

    public partial class frmProgressBar : Form
    {

        public void UpdateProgress(String strTextToDisplayOnProgress)
        {
            progressBar1.BeginInvoke(
                   new Action(() => 
                   { 
                       progressBar1.Value++; 
                       lblFileName.Text = strTextToDisplayOnProgress;
                       if (progressBar1.Value == progressBar1.Maximum)
                       {
                           this.Hide(); 
                        } 
                    }));
        }

        public delegate void DelProgress();

        public void StartProcessWithProgress(DelProgress delMethodCode, int maxCount)
        {
            InitializeProgress(maxCount);
            Thread backgroundThread = new Thread(new ThreadStart(delMethodCode));
            backgroundThread.Start();
        }
    }

    public static class PullMSI
    {
        public static frmProgressBar ExtractByMSIName(String strProductFilePath, bool reNameMSI)
        {
            frmProgressBar frmProgressBar = new frmProgressBar();

            frmProgressBar.StartProcessWithProgress(() =>
            {
                //StreamRader sr declaration and other code

                while (!sr.EndOfStream)
                {
                    //logic here
                    frmProgressBar.UpdateProgress("Copying sr.msiname");
                }
            }, 2);

            return frmProgressBar;
        }
    }
}

【问题讨论】:

  • 用户界面?这是一个控制台应用程序。
  • 我的错。抱歉,为了简化代码,我只是将所有类放在控制台应用程序中,以便我可以轻松地在这里发布。但它的 Windows 窗体应用程序和进程在按钮单击时开始。
  • 您可以使用以下任何一种:1) TPL 与任务继续 2) 重置事件 (ManualResetEventSlim/AutoResetEventSlim) 3) 使用其他机制,例如 Semaphore(强烈建议您不要这样做) 4) async/如果您正在运行.Net 4.5+,请等待。 5)生产者/消费者(对您的用例来说过度杀伤力)。基本上有多种方法可以做到这一点。阅读这些内容,然后选择一个你最喜欢的。
  • 其实我之前没有使用过这些技术,需要时间来学习。客户的期望困扰着我:(任何代码示例都会有很大帮助。
  • 您不能在 UI 事件处理程序中等待 - 它是一个状态机,必须及时为其输入队列提供服务。带有事件/信号量的阻塞等待根本不可用。像 Invoke/BeginInvoke 这样的消息信号系统更加健全。

标签: c# .net multithreading task threadpool


【解决方案1】:

我很惊讶您以前没有使用过这些,但我真的建议您阅读 C# 中的线程,因为了解复杂性和学习语言非常重要。

以下是实现您想要的三种不同方式:

1.使用重置事件(进一步阅读:https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx)。如果您的 C# 版本没有 ManualResetEventSlim,请将其替换为 ManualResetEvent 并将 Wait() 更改为 WaitOne()

class LockingWithResetEvents
{
    private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);

    public void Test()
    {
        MethodUsingResetEvents();
    }

    private void MethodUsingResetEvents()
    {
        ThreadPool.QueueUserWorkItem(_ => DoSomethingLong());
        ThreadPool.QueueUserWorkItem(_ => ShowMessageBox());
    }

    private void DoSomethingLong()
    {
        Console.WriteLine("Doing somthing.");
        Thread.Sleep(1000);
        _resetEvent.Set();
    }

    private void ShowMessageBox()
    {
        _resetEvent.WaitOne();
        Console.WriteLine("Hello world.");
    }
}

2) 使用任务并行库 (TPL)。 进一步阅读:https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

class LockingWithTPL
{
    public void Test()
    {
        Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
    }

    private void DoSomethingLong()
    {
        Console.WriteLine("Doing somthing.");
        Thread.Sleep(1000);
    }

    private void ShowMessageBox()
    {
        Console.WriteLine("Hello world.");
    }
}

3) 使用异步/等待。延伸阅读:https://msdn.microsoft.com/en-us/library/hh191443.aspx

class LockingWithAwait
{
    public void Test()
    {
        DoSomething();
    }

    private async void DoSomething()
    {
        await Task.Run(() => DoSomethingLong());
        ShowMessageBox();
    }

    private async void DoSomethingLong()
    {
        Console.WriteLine("Doing somthing.");
        Thread.Sleep(10000);
    }

    private void ShowMessageBox()
    {
        Console.WriteLine("Hello world.");
    }
}

也很高兴知道:Mutex (https://msdn.microsoft.com/en-us/library/system.threading.mutex(v=vs.110).aspx)、Semaphore (https://msdn.microsoft.com/en-us/library/system.threading.semaphore(v=vs.110).aspx)、Lock (https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx)、SemaphoreSlim (https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx)、Monitor (https://msdn.microsoft.com/en-us/library/system.threading.monitor(v=vs.110).aspx) 和 Interlocked (https://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx)。

【讨论】:

  • 你的Test 方法不需要声明Async 吗?我认为所有调用异步方法的方法也必须是异步的。 Async/Await 对我来说还是有点新,所以我可能错了。
  • @BradleyUffner 不必。我不是awaiting 任何东西。如果您不等待它,则它不需要 async 关键字,而是将其视为具有 void 返回类型的简单方法。然而,这不会编译:await DoSomething();.
  • 不,不,不等待 UI 事件处理程序!
  • @MartinJames 那是给我的吗?我发布的三个解决方案都不会阻塞我相信的调用线程。
【解决方案2】:

如果您使用的是 .NET 4.0(带有 VS2012)或更高版本,您可以使用 Task Parallel Libraryasync-await 轻松完成此操作:

private async void button1_Click(object sender, EventArgs e)
{
    frmProgressBar frmProgressBarObj = await Task.Run(() =>
                      PullMSI.ExtractByMSIName("products.txt", false));

    MessageBox.Show(string.Format("Returned {0}", frmProgressBarObj.ToString());
}

对于 .NET 4,您需要添加 Microsoft.Bcl.Async

【讨论】:

  • 值得注意的是,仅当作者使用 .NET 4.5 时,这是一个选项
  • @TomC。 .NET 4.0 还支持async-awaitMicrosoft.Bcl.Async
  • @YuvalItzchakov 对于“支持”的一些值
  • 它不是原生的,需要对 .NET 4 进行大量修改和调整。不是您想要的方法,尤其是对于更复杂的用例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
相关资源
最近更新 更多