Solved

Need help understanding threads....

Posted on 2006-07-03
4
423 Views
Last Modified: 2010-04-16
Here's the problem....

I need to have 4 worker threads and a main thread.  Each of the worker threads continually moniter a COM port for input.  Any time they get input, they add that input to a list and notify the main thread that something has been added to the list.  The main thread should then look at the list and see what was added.

How do I do this?  I've looked at several examples, but none of them seem to cover this particular issue.

The rest of the post is what I've tried.  I keep getting errors about controls only being accessable by the thread that created them.  I *thought* the purpose of raising an event was so that threads could communicate.  What am I doing wrong?

namespace MyStuff
{
    public class StringListWithEvents : List<string>
    {
        public delegate void ListAddEventHandler();
        public event ListAddEventHandler ListAdded;

        public virtual void OnListAdd()
        {
            if (ListAdded != null)
            {
                ListAdded();
            }
        }
    }

public class MainForm
{
   protected ThreadStart myThreadStart;
   protected Thread myThread;

   public StringListWithEvents myList = new StringListWithEvents();

   public void InitializeComponent()
   {
            private System.Windows.Forms.ListBox myListBox;
            myListBox = new System.Windows.Forms.ListBox();
   }

   public MainForm()
   {      
            InitializeComponent();
            m_strlstIDCard.ListAdded += new StringListWithEvents.ListAddEventHandler(ItemAdded);
            myThreadStart = new ThreadStart(MyThreadStuff)
            myThread = new Thread(myThreadStart);
            myThread.Start();            
   }
   
   public void ItemAdded()
   {
        foreach (string a in myList)
            myListBox.Items.Add(a); //this is where it bombs with the error
   }

}
0
Comment
Question by:Kevon
  • 2
  • 2
4 Comments
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 17034432
When you receive your event you need to use a delegate and the Invoke() method to marshal the event from the calling thread onto the main UI thread where it is safe to update the GUI.

See this thread for a simplified example:
http://www.experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_21713176.html
0
 
LVL 2

Author Comment

by:Kevon
ID: 17036939
Thank you for the comment.  Would you mind copying and pasting the solution here.  I am not currently subscribed, and therefore can not see the solution to that issue.

Thanks,

Kevon
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 500 total points
ID: 17037073
Sure...here is the whole thing...

Title: C# 2.0 Threading Question
asked by dogsdieinhotcars on 01/27/2006 12:20PM MST  
This solution was worth  500 Points and received a grade of A  

I have a probably common threading issue, but can't get my head around how to easily deal with it.  

On a winform, I have a grid and on the bottom of the form I have detail information about the current item on the grid that is populated from a web service.  The web service is pretty slow, so if a user is just scrolling up and down in the grid, I don't want them to be slowed by waiting for the slow web service that populates the bottom part of the form.  Hence I thought, this is a good case for a thread.

So I tried the background worker which wasn't appropriate because I couldn't "kill" it off --- so if a user scrolled up and the background thread from the prior item hadn't finished - an error is thrown.

Now I've tried good old standard thread.  I seem to not be doing it right because I get an error for updating the UI from a different thread than it was created on.  However, there should not be any deadlock situation ever because I'd like the thread to die immediately when the user scrolls to the next record --- i.e. there should only be one background thread ever at once and if it hasn't finished by the time the user goes to the next record, I'd like to destroy it and start it again.

How can I go about this?  Any psuedo/basic code would be helpful.

Main Thread:
  (Grid Position Changed)
      Is background thread active?  
          kill background thread.
     Activate Background Thread to populate bottom of form.

Background Thread:
   Call Slow web service.
   Populate bottom of form.

That's my psuedo code, what should I be doing to make that happen in C#?


 
   
Accepted Answer from Idle_Mind
Date: 01/27/2006 03:30PM MST
Grade: A

"I get an error for updating the UI from a different thread than it was created on."

Encapsulate the thread in a class and make that class raise an event that the main form can subscribe to.  Use a delegate to marshal the event onto the UI thread where you can safely update the UI controls.

You can do this without a class if you want to....just make sure to follow the type of construction shown in the UpdateTime() method and use .InvokeRequired() with a Delegate if necessary.

Here is a simple example...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        Class1 c;

        private void Form1_Load(object sender, EventArgs e)
        {
            c = new Class1();
            c.currentTime += new TimeHack(this.UpdateTime);
        }

        private void UpdateTime(string time)
        {
            if (label1.InvokeRequired)
            {
                TimeHack t = new TimeHack(this.UpdateTime);
                this.Invoke(t, new object[] { time });
            }
            else
            {
                label1.Text = time;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            c.Abort();
        }

    }

    public delegate void TimeHack(string hack);

    class Class1
    {
        public event TimeHack currentTime;

        private Thread t;
        private bool abortThread = false;

        public Class1()
        {
            t = new Thread(new ThreadStart(ThreadLoop));
            t.Start();
        }

        private void ThreadLoop()
        {
            while (!abortThread )
            {
                currentTime(DateTime.Now.ToString());
                System.Threading.Thread.Sleep(1000);
            }
        }

        public void Abort()
        {
            abortThread = true;
        }
    }
}



 
Assisted Answer from devsolns
Date: 01/27/2006 04:21PM MST
Grade: A

I would use an asynchronous web service call instead.

Asynchronous Web Service Calls over HTTP with the .NET Framework
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnservice/html/service09032002.asp

if you need help delegates let me know.  you'll find them much more flexible than creating threads.  i will say that pay close attention above to the fact that idle_minds code does not attempt to interact direction with winforms while using a different thread than the one that created the control.  that is one of the most important thing to know when working w/ winforms.  they are not thread safe!



 
Comment from dogsdieinhotcars
Date: 01/28/2006 11:23AM MST

Ok, I think I've about got it...  I guess what I'm not getting (this is slow getting through my thick skull - sorry!!) is how do I dump the background thread -immediately - and start over when the user goes to the next record in the grid (or if I should be going with another plan).  So, when the user hits a record on a grid, and the background thread may take three seconds to pull down the info for the detail portion --- but if the user has gone to another record on the grid, then I'd want to immediately kill that background thread and start a new one to get the new detail info for the current record...does that make sense or am I talking in gibberish??  



 
Comment from Idle_Mind
Date: 01/28/2006 03:15PM MST

What you're saying makes perfect sense...

The ability to cancel the pending thread is really dependent upon what you are doing inside of it.  If the work being done is a long "blocking call" (the code stops at a particular line until data is returned) then you can't really cancel it.  But if the work being done in the thread can be monitored by a polling loop, then you can toggle a flag and exit the loop.

Consider this "simulated" example.  Create a new project with a ListBox, a Label and a BackgroundWorker component.

        private void Form1_Load(object sender, EventArgs e)
        {
            int i;
            for (i = 1; i < 11; i++)
                listBox1.Items.Add("Item" + i);
        }

        private int index;
        private bool cancelled;
        private string data;

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                cancelled = true;
                while (backgroundWorker1.IsBusy)
                {
                    System.Threading.Thread.Sleep(100);
                    Application.DoEvents();
                }
            }
            index = listBox1.SelectedIndex;
            cancelled = false;
            backgroundWorker1.RunWorkerAsync();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            // A simulated long process...
            // The length of time to complete is directly proportional
            // to the index of the item selected in the ListBox

            // possibly make an asynchronous call here
            // and toggle a flag when it is complete
            // monitor that flag and the cancelled flag below
            // in the polling loop

            DateTime dt = DateTime.Now.AddSeconds(index + 1);            
            do
                System.Threading.Thread.Sleep(100);
            while (!cancelled & ((TimeSpan)dt.Subtract(DateTime.Now)).TotalMilliseconds > 0);

            if (!cancelled)
            {
                data = "Item" + (index + 1) + " data update.";
            }
           
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!cancelled)
            {
                label1.Text = data;
            }          
        }



 
Comment from devsolns
Date: 01/28/2006 03:33PM MST

In case someone here happends to mention it, I will tell you ahead of time DO NOT use Thread.Abort();  There really is not a safe way to tell you thread so quit.  How are resources to be cleaned up?  What you need to do is architecturally design a solution to resolve this.  First the idea of calling a web service for each record is not a good idea.  If network traffic is slow or the server is on high load the user experience is going to be pretty bad regardless of whether your using threads or not.  Without putting to much though into the best solution I think the following would be much better.  Instead of calling a web service to retrieve detailed information for each record you should call a web service to retrieve detail information for a large number of the records.  So when the app starts it goes and retrieves detail information for the first 100 records.  The web service would not be called again until the user gets to the next 100.

Depending on how your getting the detail information (database speed) I would almost guarantee that it takes almost the same amount of time to return hundreds of detail records as it does to return 1.  These numbers of course are made up.  You might so no difference in getting 1000 records.  It depends on a lot of things.  Either way I think youll find this to work much better.  If anything it takes care of your worry about the user going to the next record and having to stop the last ws call.  The chances of the user going to the next 100th, 300th, or 500th record before the previous web service call is pretty unlikely and if it does happen let it continue and still populate the old records in case they decide to scroll back up.

good luck,
gary paulish



 
Comment from dogsdieinhotcars
Date: 01/28/2006 08:44PM MST

Thanks for the help and advice.  It's going to take me a day or two to process/understand this (again, I think I was dropped on the head as a child or something), but I decided to award points before I've got it down so I don't make you guys wait while I figure it out  Thanks again!!
0
 
LVL 2

Author Comment

by:Kevon
ID: 17038501
Thank you very much.  I have not had the chance to try your solution yet, but it makes a lot of sense, so I'm confident in awarding the points.

Kevon
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Bit flags and bit flag manipulation is perhaps one of the most underrated strategies in programming, likely because most programmers developing in high-level languages rely too much on the high-level features, and forget about the low-level ones. Th…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now