How to wait for background worker to finish processing?

前端 未结 5 649
闹比i
闹比i 2021-01-19 17:07

I have 3 background workers each processing a channel of a 24-bit Bitmap image (Y, Cb, Cr). The processing for each 8-bit image takes several seconds and they might not com

相关标签:
5条回答
  • 2021-01-19 17:42

    Consider using AutoResetEvents:

    private void button1_Click(object sender, EventArgs e)
        {
            var e1 = new System.Threading.AutoResetEvent(false);
            var e2 = new System.Threading.AutoResetEvent(false);
            var e3 = new System.Threading.AutoResetEvent(false);
    
            backgroundWorker1.RunWorkerAsync(e1);
            backgroundWorker2.RunWorkerAsync(e2);
            backgroundWorker3.RunWorkerAsync(e3);
    
    
            // Keep the UI Responsive
            ThreadPool.QueueUserWorkItem(x =>
            {
                // Wait for the background workers
                e1.WaitOne();
                e2.WaitOne();
                e3.WaitOne();
                MethodThatNotifiesIamFinished();
            });
    
            //Merge Code
        }
    
    
        void BackgroundWorkerMethod(object obj)
        {
            var evt = obj as AutoResetEvent;
            //Do calculations
            etv.Set();
        }
    

    This way you do not waste cpu time in some loops & using a seperate thread for waiting keeps the UI Responsive.

    0 讨论(0)
  • 2021-01-19 17:46

    Building on the answer from Renuiz, I would do it this way:

    private object lockObj;
    
    private void backgroundWorkerN_RunWorkerCompleted(
        object sender, 
        RunWorkerCompletedEventArgs e)
    {
        lock (lockObj)
        {
            y = true;
            if (cb && cr) // if cb and cr flags are true - 
                          // other backgroundWorkers finished work
            {
                someMethodToDoOtherStuff();
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-19 17:46

    I would use three threads instead of background workers.

    using System.Threading;
    
    class MyConversionClass
    {
        public YCBCR Input;
        public RGB Output
    
        private Thread Thread1;
        private Thread Thread2;
        private Thread Thread3;
    
        private int pCompletionCount;
    
        public MyConversionClass(YCBCR myInput, RGB myOutput)
        {
            this.Input = myInput;
            this.Output = myOutput;
    
            this.Thread1 = new Thread(this.ComputeY);
            this.Thread2 = new Thread(this.ComputeCB);
            this.Thread3 = new Thread(this.ComputeCR);
        }
    
        public void Start()
        {
            this.Thread1.Start();
            this.Thread2.Start();
            this.Thread3.Start();
        }
    
        public void WaitCompletion()
        {
            this.Thread1.Join();
            this.Thread2.Join();
            this.Thread3.Join();
        }
    
        // Call this method in background worker 1
        private void ComputeY()
        {
            // for each pixel do My stuff
            ...
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        // Call this method in background worker 2
        private void ComputeCB()
        {
            // for each pixel do My stuff
            ...
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        // Call this method in background worker 3
        private void ComputeCR()
        {
            // for each pixel do My stuff
            ...
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        private void MergeTogether()
        {
            // We merge the three channels together
            ...
        }
    }
    

    Now in your code you simply do this:

    private void button1_Click(object sender, EventArgs e)
    {
        MyConversionClass conversion = new MyConversionClass(myinput, myoutput);
        conversion.Start();
        conversion.WaitCompletion();
    
        ... your other stuff
    }
    

    However this will pause your GUI until all operations are completed. I would use SynchronizationContext instead to notify the GUI that the operation has completed.

    This version uses SynchronizationContext for synchronizing the GUI thread without waiting at all. This will keep the GUI responsive and performs the entire conversion operation in the other threads.

    using System.Threading;
    
    class MyConversionClass
    {
        public YCBCR Input;
        public RGB Output
    
        private EventHandler Completed;
    
        private Thread Thread1;
        private Thread Thread2;
        private Thread Thread3;
        private SynchronizationContext SyncContext;
    
        private volatile int pCompletionCount;
    
        public MyConversionClass()
        {
            this.Thread1 = new Thread(this.ComputeY);
            this.Thread2 = new Thread(this.ComputeCB);
            this.Thread3 = new Thread(this.ComputeCR);
        }
    
        public void Start(YCBCR myInput, RGB myOutput, SynchronizationContext syncContext, EventHandler completed)
        {
            this.SyncContext = syncContext;
            this.Completed = completed;
            this.Input = myInput;
            this.Output = myOutput;
    
            this.Thread1.Start();
            this.Thread2.Start();
            this.Thread3.Start();
        }
    
        // Call this method in background worker 1
        private void ComputeY()
        {
            ... // for each pixel do My stuff
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        // Call this method in background worker 2
        private void ComputeCB()
        {
            ... // for each pixel do My stuff
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        // Call this method in background worker 3
        private void ComputeCR()
        {
            ... // for each pixel do My stuff
            if (Interlocked.Increment(ref this.CompletionCount) == 3)
                this.MergeTogether();
        }
    
        private void MergeTogether()
        {
            ... // We merge the three channels together
    
            // We finish everything, we can notify the application that everything is completed.
            this.syncContext.Post(RaiseCompleted, this);
        }
    
        private static void RaiseCompleted(object state)
        {
            (state as MyConversionClass).OnCompleted(EventArgs.Empty);
        }
    
        // This function is called in GUI thread when everything completes.
        protected virtual void OnCompleted(EventArgs e)
        {
            EventHandler completed = this.Completed;
            this.Completed = null;
            if (completed != null)
                completed(this, e);
        }
    }
    

    Now, in your code...

    private void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
    
        MyConversionClass conversion = new MyConversionClass();
        conversion.Start(myinput, myoutput, SynchronizationContext.Current, this.conversion_Completed);
    }
    
    private void conversion_Completed(object sender, EventArgs e)
    {
        var output = (sender as MyConversionClass).Output;
        ... your other stuff that uses output
    
        button1.Enabled = true;
    }
    

    The good things of both method is that they are GUI agnostic, you can put them in a library and keep your precious multi-threading conversion code totally independant on the GUI you are using, that is, WPF, Web or Windows Forms.

    0 讨论(0)
  • 2021-01-19 17:50

    You can use WaitHandle.WaitAll in conjunction with EventWaitHandle to achieve what you need. Herein enclosed a code sample which does what I mentioned. The enclosed code is just an outline of how the solution will look like. You must add proper exception handling and defensive approach to make this code more stable.

    using System;
    using System.ComponentModel;
    using System.Threading;
    
    namespace ConsoleApplication7
    {
        class Program
        {
            static void Main(string[] args)
            {
                BWorkerSyncExample sample = new BWorkerSyncExample();
                sample.M();
            }
        }
        class BWorkerSyncExample
        {
            BackgroundWorker worker1, worker2, worker3;
            EventWaitHandle[] waithandles;
    
            public void M()
            {
                Console.WriteLine("Starting background worker threads");
                waithandles = new EventWaitHandle[3];
    
                waithandles[0] = new EventWaitHandle(false, EventResetMode.ManualReset);
                waithandles[1] = new EventWaitHandle(false, EventResetMode.ManualReset);
                waithandles[2] = new EventWaitHandle(false, EventResetMode.ManualReset);
    
                StartBWorkerOne();
                StartBWorkerTwo();
                StartBWorkerThree();
    
                //Wait until all background worker complete or timeout elapse
                Console.WriteLine("Waiting for workers to complete...");
                WaitHandle.WaitAll(waithandles, 10000);
                Console.WriteLine("All workers finished their activities");
                Console.ReadLine();
            }
    
            void StartBWorkerThree()
            {
                if (worker3 == null)
                {
                    worker3 = new BackgroundWorker();
                    worker3.DoWork += (sender, args) =>
                                        {
    
                                            M3();
                                            Console.WriteLine("I am done- Worker Three");
                                        };
                    worker3.RunWorkerCompleted += (sender, args) =>
                                        {
                                            waithandles[2].Set();
                                        };
    
                }
                if (!worker3.IsBusy)
                    worker3.RunWorkerAsync();
            }
    
            void StartBWorkerTwo()
            {
                if (worker2 == null)
                {
                    worker2 = new BackgroundWorker();
                    worker2.DoWork += (sender, args) =>
                                           {
    
                                               M2();
                                               Console.WriteLine("I am done- Worker Two");
                                           };
                    worker2.RunWorkerCompleted += (sender, args) =>
                                           {
                                               waithandles[1].Set();
                                           };
    
                }
                if (!worker2.IsBusy)
                    worker2.RunWorkerAsync();
            }
    
            void StartBWorkerOne()
            {
                if (worker1 == null)
                {
                    worker1 = new BackgroundWorker();
                    worker1.DoWork += (sender, args) =>
                                           {
    
                                               M1();
                                               Console.WriteLine("I am done- Worker One");
                                           };
                    worker1.RunWorkerCompleted += (sender, args) =>
                                           {
                                               waithandles[0].Set();
                                           };
    
                }
                if (!worker1.IsBusy)
                    worker1.RunWorkerAsync();
            }
            void M1()
            {
               //do all your image processing here.
            //simulate some intensive activity.
            Thread.Sleep(3000);
            }
            void M2()
            {
              //do all your image processing here.
            //simulate some intensive activity.
            Thread.Sleep(1000);
            }
            void M3()
            {
             //do all your image processing here.
            //simulate some intensive activity.
            Thread.Sleep(4000);
            }
    
        }
    }
    
    0 讨论(0)
  • 2021-01-19 17:54

    Maybe you could set and check flags in background worker complete event handlers. For example:

    private void backgroundWorkerN_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        y = true;
        if(cb && cr)//if cb and cr flags are true - other backgroundWorkers finished work
           someMethodToDoOtherStuff();
    }
    
    0 讨论(0)
提交回复
热议问题