web-dev-qa-db-fra.com

Comment attendre la fin du thread sans bloquer l'interface utilisateur

Je veux que mon programme attende après la ligne ci-dessous

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

comme ci-dessus, la méthode appelle en interne le thread via la méthode StartProcessWithProgress (). Je veux que ce thread soit terminé avant que // la logique de code -2 ne soit exécutée. Dans le même temps, il ne doit pas arrêter la mise à jour de l'interface utilisateur effectuée par frmProgressBar.UpdateProgress (). Comment puis-je faire cela?

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;
        }
    }
}
12
Ashif Nataliya

Je suis très surpris que vous n'ayez jamais travaillé avec aucun d'entre eux auparavant, mais je recommanderais vraiment de lire sur le filetage en C # car il est fondamentalement important de comprendre les subtilités et d'apprendre le langage.

Voici trois façons différentes de réaliser ce que vous voulez:

1. Utilisation des événements de réinitialisation (lecture supplémentaire: https://msdn.Microsoft.com/en-us/library/system.threading.manualreseteventslim (v = vs.110) .aspx ). Si votre version C # n'a pas le ManualResetEventSlim, remplacez-le par ManualResetEvent et changez Wait() par 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) Utilisation de la bibliothèque parallèle de tâches (TPL). Pour en savoir plus: 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) Utilisation d'Async/Await. Pour en savoir plus: 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.");
    }
}

Aussi bon à savoir: 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 ) et Interlocked ( https://msdn.Microsoft.com/en-us/library/system.threading.interlocked (v = vs.110) .aspx ).

24
kha

Si vous utilisez .NET 4.0 (avec VS2012) ou supérieur, vous pouvez le faire assez facilement avec Task Parallel Library et async-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());
}

Pour .NET 4, vous devrez ajouter Microsoft.Bcl.Async .

2
Yuval Itzchakov