Search code examples
c#.netmultithreadingtaskthreadpool

How to wait for thread to complete without blocking UI


I want my program to wait after below line

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

as above method is internally calling thread through StartProcessWithProgress() method . I want that thread to be completed before //code logic -2 line gets executed. At the same time, It should not stop UI update done by frmProgressBar.UpdateProgress(). How do I do this?

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

Solution

  • Below are three different ways you can achieve what you want:

    1. Using reset events (further reading). If your C# version doesn't have the ManualResetEventSlim, replace it with ManualResetEvent and change Wait() with 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 something.");
            Thread.Sleep(1000);
            _resetEvent.Set();
        }
    
        private void ShowMessageBox()
        {
            _resetEvent.WaitOne();
            Console.WriteLine("Hello world.");
        }
    }
    

    2) Using Task Parallel Library (TPL). (Further reading)

    class LockingWithTPL
    {
        public void Test()
        {
            Task.Factory.StartNew(DoSomethingLong).ContinueWith(result => ShowMessageBox());
        }
    
        private void DoSomethingLong()
        {
            Console.WriteLine("Doing something.");
            Thread.Sleep(1000);
        }
    
        private void ShowMessageBox()
        {
            Console.WriteLine("Hello world.");
        }
    }
    

    3) Using Async/Await. (Further reading)

    class LockingWithAwait
    {
        public void Test()
        {
            DoSomething();
        }
    
        private async void DoSomething()
        {
            await Task.Run(() => DoSomethingLong());
            ShowMessageBox();
        }
    
        private async void DoSomethingLong()
        {
            Console.WriteLine("Doing something.");
            Thread.Sleep(10000);
        }
    
        private void ShowMessageBox()
        {
            Console.WriteLine("Hello world.");
        }
    }
    

    Also good to know: Mutex, Semaphore, lock, SemaphoreSlim, Monitor and Interlocked.