Search code examples
c#multithreadinguser-interfacedeadlock

Thread progress to GUI


I implemented threading in my application for scraping websites. After all the sites are scraped I want to process them.

form creates queueworker(which creates 2 workers and processes tasks). After all the tasks are done I want to process them baack in the formthread. At this point I accieved this with

public void WaitForCompletion()
    {
        // Enqueue one null task per worker to make each exit.
        StopWorkers();
        
        //wait for the workers to finish
        foreach (Thread worker in workers)
        {
            worker.Join();
        }
    }

After a task is preformed I fire (from queueworker):

public event EventHandler<ProgressEvent> UrlScanned;   
 
if (UrlScanned != null)
{ 
         UrlScanned(this, new ProgressEvent(task.Name, 1));
}

And catch that event with:

urlscanner.UrlScanned += new EventHandler<ProgressEvent>(UrlScanningProgress);     

private void UrlScanningProgress(object sender, ProgressEvent args)
{
   if (pbarurlScan.InvokeRequired)
   {
       //don't use invoke when Thread.Join() is used! Deadlock
       Invoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args); 
       //BeginInvoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args)};
   }
   else
   {
                pbarurlScan.Value++;
   }
}

The problem is that the formthread gets blocked and with calling Invoke the whole application is now in a deadlock situation.

How can I give update to the formthread without having a deadlock and have an immidiate update (begininvoke occurs if the workers threads are done)


Solution

  • Why are you doing the Join? I would raise a callback at the end of each thread - perhaps decrementing a counter. When the counter gets to 0, then call back to the UI thread and do the "all done" code; something like:

    using System.Windows.Forms;
    using System.Threading;
    using System;
    class MyForm : Form
    {
    
        public MyForm()
        {
            Button btn = new Button();
            Controls.Add(btn);
            btn.Text = "Go";
            btn.Click += btn_Click;
        }
        int counter;
        void btn_Click(object sender, System.EventArgs e)
        {
            for (int i = 0; i < 5; i++)
            {
                Interlocked.Increment(ref counter);
                ThreadPool.QueueUserWorkItem(DoWork, i);
            }
        }
        void DoWork(object state)
        {
            for (int i = 0; i < 10; i++)
            { // send progress
                BeginInvoke((Action)delegate { Text += state.ToString(); });
                Thread.Sleep(500);
            }
            EndThread(); // this thread done
        }
        void EndThread()
        {
            if (Interlocked.Decrement(ref counter) == 0)
            {
                AllDone();
            }
        }
        void AllDone()
        {
            Invoke((Action)delegate { this.Text += " all done!"; });
        }
        [STAThread]
        static void Main()
        {
            Application.Run(new MyForm());
        }
    }