Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 12 of 12

Thread: Threading

  1. #1
    Senior Coder alykins's Avatar
    Join Date
    Apr 2011
    Posts
    1,718
    Thanks
    41
    Thanked 191 Times in 190 Posts

    Threading

    I am trying to get multithreading more unraveled in my head. I made these three classes.

    A global variable class
    Code:
    public partial class globes
    {
        public bool[] sets = new bool[] { false, false, false };
        public bool boolChanged = false;
        public string tmpStr = string.Empty;
        public int gcount = 0;
        public bool intChanged = false;
        public Random r = new Random();
        public bool gDone = false;
        public bool first = true;
    }
    Drop in point
    Code:
    class Driver
        {
            static void Main(string[] args)
            {
                Console.WriteLine("start");
    
                globes g = new globes();
    
                Thread[] threads = new Thread[6];
                ParameterizedThreadStart[] pts = new ParameterizedThreadStart[6];
    
                lockMe _lockme = new lockMe();
    
                for (int b = 0; b < 3; b++)
                {
                    pts[b] = new ParameterizedThreadStart(_lockme.paramThreadStarter);
                    threads[b] = new Thread(pts[b]);
                    threads[b].Name = string.Format("{0}", b);
                    threads[b].Start(b);
                }
            }
        }
    And then my threading class
    Code:
    class lockMe
        {
            #region Fields
    
            private string[] words = new string[] {"string0", "string1", "string2", "string3"};
            private globes g = new globes();
            private object myKey = new object();
    
            private string[] name = new string[] { String.Empty, String.Empty, String.Empty };
    
            #endregion
    
            #region methods
    
            // first called for all threads
            private void setName(Int16 i)
            {
                Monitor.Enter(myKey);
                {
                    try
                    {
                        name[i] = string.Format("{0}:{1}", Thread.CurrentThread.Name, g.r.Next(100, 500).ToString());
                    }
                    finally
                    {
                        Monitor.PulseAll(myKey);
                        Monitor.Exit(myKey);
                    }
                }
            }
    
            // thread 1
            private void changeBool(Int16 a)
            {
                Monitor.Enter(myKey);
                {
                    try
                    {
                        int i = getBools();
                        //Thread.Sleep(3000);
                        if (g.gcount > 5) { g.gDone = true; return; }
                        if (i == 3) resets();
                        else { for (int x = 0; x <= i; i++) { g.sets[x] = true; } }
    
                        Console.WriteLine("Thread {0} ran through changeBool()\n", name[a]);
                    }
                    finally
                    {
                        Monitor.PulseAll(myKey);
                        Monitor.Exit(myKey);
                    }
                }
            }
    
            // thread 2
            private void changeInt(Int16 i)
            {
                Monitor.Enter(myKey);
                {
                    try
                    {
                        g.gcount++;
                        //Thread.Sleep(g.r.Next(1000, 3000));
                        Console.WriteLine("Thread {0}: Count is now at {1}\n", name[i], g.gcount);
                    }
                    finally
                    {
                        Monitor.PulseAll(myKey);
                        Monitor.Exit(myKey);
                    }
                }
            }
    
            // thread 3
            private void printString(Int16 i)
            {
                Monitor.Enter(myKey);
                {
                    try
                    {
                        Console.WriteLine("...incoming...");
                        //Thread.Sleep(g.r.Next(1500, 2500));
                        Console.WriteLine("Thread {0} printing...{1}\n", name[i], words[g.r.Next(0, 3)]);
                    }
                    finally
                    {
                        Monitor.PulseAll(myKey);
                        Monitor.Exit(myKey);
                    }
                }
            }
    
            // not locked- called from within a locked peice
            private int getBools()
            {
                if ((g.sets[0] == false) && (g.sets[1] == false) && (g.sets[2] == false)) return 0;
                else if ((g.sets[0] == true) && (g.sets[1] == false) && (g.sets[2] == false)) return 1;
                else if ((g.sets[2] == true) && (g.sets[3] == false)) return 2;
                else if ((g.sets[0] == true) && (g.sets[1] == true) && (g.sets[2] == true)) return 3;
                else return 99;
            }
    
            // should not need locks- called within locked statement
            private void resets()
            {
                if (g.first) { Console.WriteLine("FIRST!!"); g.first = false; }
                else Console.WriteLine("Cycle has reset...");
            }
    
            private bool getStatus()
            {
                bool x = false;
    
                Monitor.Enter(myKey);
                {
                    try
                    {
                        x = g.gDone;
                    }
                    finally
                    {
                        Monitor.PulseAll(myKey);
                        Monitor.Exit(myKey);
                    }
                }
    
                return x;
            }
    
            #endregion
    
            #region Constructors
    
            public void paramThreadStarter(object starter)
            {
                Int16 i = Convert.ToInt16(starter);
                setName(i);
    
                do
                {
                    switch (i)
                    {
                        default: throw new Exception();
                        case 0:
                            changeBool(i);
                            break;
                        case 1:
                            changeInt(i);
                            break;
                        case 2:
                            printString(i);
                            break;
                    }
                } while (!getStatus());
    
                Console.WriteLine("fin");
                Console.ReadLine();
            }
    
            #endregion
        }
    So I have a few questions. The first- is it better to have my global class set like this? Or should I be using a static class with properties and altering them that way? Next question is, when this runs, at random one of the threads will run, pulse/exit the lock, and then step right back in (sometimes like 5-10 times before the next thread picks up the lock). Why does this happen?

    I code C hash-tag .Net
    Reference: W3C W3CWiki .Net Lib
    Validate: html CSS
    Debug: Chrome FireFox IE

  • #2
    Rockstar Coder
    Join Date
    Jun 2002
    Location
    USA
    Posts
    9,074
    Thanks
    1
    Thanked 328 Times in 324 Posts
    You should avoid globals all together when possible; they aren't needed as often as people think. If you do need one, I would use the singleton pattern. You could use a static class but I personally prefer to use those for utility type functions that have no data storage. Right now your "globals" aren't the same instance between your main function and the ones in the lockMe instance.

    For this example you don't need to use PulseAll. And instead of using Monitor I would use lock instead.
    Code:
            /// <summary>first called for all threads</summary>
            /// <param name="i">The thread number.</param>
            private void setName(Int16 i)
            {
                lock(myKey)
                {
                    name[i] = string.Format("{0}:{1}", Thread.CurrentThread.Name, g.r.Next(100, 500).ToString());
                }
            }
    There are more things I would change in your code as well. I can elaborate on them if you want.
    OracleGuy

  • #3
    Senior Coder alykins's Avatar
    Join Date
    Apr 2011
    Posts
    1,718
    Thanks
    41
    Thanked 191 Times in 190 Posts
    Yes please. To elaborate on why I have the globals... This is the ground pattern for a much larger application. Basically I will have ~4 separate parts

    Part 1
    Winform UI that constantly pulls from the 'globals' and updates fields accordingly

    Part 2
    Constant input stream of data for a PLC

    Part 3
    Constant input stream for scans

    part 4
    Constant output to PLC

    Basically part 3 will update variables within globals. On each cycle part 2 will check those variables, and if a valid read, send data through BI layer and process, interrupt part 4 to send message. Part 2 is on a separate thread, because checking those values is just a small piece of what it needs to do. I am quickly running into an issue of my threads needing to cross talk but their base class being different :/

    Sorry that this is kind of 'word vomit'... I am still working through the logic as I am working through the threading understanding lol

    I code C hash-tag .Net
    Reference: W3C W3CWiki .Net Lib
    Validate: html CSS
    Debug: Chrome FireFox IE

  • #4
    Rockstar Coder
    Join Date
    Jun 2002
    Location
    USA
    Posts
    9,074
    Thanks
    1
    Thanked 328 Times in 324 Posts
    Ok, the context you provided helps. If part 2 is just validating the information from part 3, why not just combine them together? Unless there is a large amount of data where the validation can be parallelized, it seems simpler to combine them together.

    What is the "BI Layer"?

    So the win form would run on the main application thread and would there be two other threads? One for reading from the device and one for writing? Is the communication channel to the PLC full duplex?

    For sharing information between the threads you could create a class that would act as a shared context. That class would need to be thread safe. Then you can read and write the values in the worker threads without having to worry about locking them since that would be wrapped up in the shared context class.

    A couple general suggestions based on the code you posted. I'm not trying to be condescending.
    • Don't use single letter variable names. Once the code gets complicated it just makes your life a lot harder. Especially if you put the code down for a few weeks/months/years and then have to work on it again.
    • I would recommend using the collection objects such as List<> instead of using arrays.
    • Instead of public fields on a class use properties instead. There is no performance difference and then properties lets you restrict the accessibility of the setter as needed.
    OracleGuy

  • #5
    Senior Coder alykins's Avatar
    Join Date
    Apr 2011
    Posts
    1,718
    Thanks
    41
    Thanked 191 Times in 190 Posts
    @Oracleguy: I sent you a PM with different question as well...

    Is there a hit for using Monitor as opposed to lock()? The reason why is for some reason the structure of the Monitor makes more sense to me and is more readable (I know that may sound odd)... Also, this may sound like a stupid question:
    Right now your "globals" aren't the same instance between your main function and the ones in the lockMe instance.
    That is a problem I am not liking, which is why I was contemplating making it a static class, is there a way to make one global instance in main class and then reference it throughout? and... keeping with that 'thought pattern' ... is there a way that I could make my lock object part of "said global class" and then be accessible to any other thread that may need to lock on it?

    example of what I have in head...
    Code:
    class globes
        object globallock
        bool[4]  flagBools { f f f f } // accessed by all
        
        int[] threadAInts 1 2 3 4
        string[] threadBstrings blah blah blah
    end Class
    
    --------------------------------------------------
    
    static void Main()
        globes g = new globes
        
        // create threading objects
        // kick off threads
    end Class
    
    --------------------------------------------------
    
    class threadA
        private object lockme
        
        meth 1
           // do something with int[]
           lock(lockme)
        
        meth 2
           // do something with g.bool[]
           lock(g.globallock)
    end Class
    
    --------------------------------------------------
    
    class threadB
        meth 1
           // do something with string[]
           lock(lockme)
        
        meth 2
           // do something with g.bool[]
           lock(g.globallock)
    end Class

    I code C hash-tag .Net
    Reference: W3C W3CWiki .Net Lib
    Validate: html CSS
    Debug: Chrome FireFox IE

  • #6
    Rockstar Coder
    Join Date
    Jun 2002
    Location
    USA
    Posts
    9,074
    Thanks
    1
    Thanked 328 Times in 324 Posts
    Using lock instead of Monitor just requires less code and prevents threading issues due to accidental coding errors since it handles the try finally block and the entering/exit for you.

    For your global class take a look at this: Singleton Design Pattern in C# and VB.NET.

    I think you are thinking about how the locking should work a little backwards. Instead of the threading having to access the lock object so it can change the values in the global class, you should have a method on the global class that lets the caller change the values. Then internally in the global class you do the locking the make the method thread safe. Did that make sense or am I being confusing?
    OracleGuy

  • Users who have thanked oracleguy for this post:

    alykins (03-07-2014)

  • #7
    Senior Coder alykins's Avatar
    Join Date
    Apr 2011
    Posts
    1,718
    Thanks
    41
    Thanked 191 Times in 190 Posts
    No not confusing, that actually makes a lot of sense. So essentially I can have as many threads running wild as I want so long as the variable class handles their incoming change requests right? I'm reading through the link you provided now

    I code C hash-tag .Net
    Reference: W3C W3CWiki .Net Lib
    Validate: html CSS
    Debug: Chrome FireFox IE

  • #8
    Rockstar Coder
    Join Date
    Jun 2002
    Location
    USA
    Posts
    9,074
    Thanks
    1
    Thanked 328 Times in 324 Posts
    Quote Originally Posted by alykins View Post
    No not confusing, that actually makes a lot of sense. So essentially I can have as many threads running wild as I want so long as the variable class handles their incoming change requests right? I'm reading through the link you provided now
    Correct. That will properly encapsulate the functionality.
    OracleGuy

  • #9
    New Coder
    Join Date
    Aug 2013
    Posts
    29
    Thanks
    1
    Thanked 6 Times in 6 Posts
    Why do you write Monitor.PulseAll?

  • #10
    New Coder
    Join Date
    Jan 2013
    Location
    Canada
    Posts
    27
    Thanks
    0
    Thanked 0 Times in 0 Posts
    Why not use the generic ConcurrentBag<T> class instead of an array of string's? And you also have the task parallel library (TPL).
    Microsoft MVP .NET Programming - (2012 - Present)
    ®Crestron DMC-T Certified Automation Programmer & Software Developer

  • #11
    Senior Coder alykins's Avatar
    Join Date
    Apr 2011
    Posts
    1,718
    Thanks
    41
    Thanked 191 Times in 190 Posts
    @hBGI: because
    I am trying to get multithreading more unraveled in my head.
    and I don't know any better? I thought it was needed in lieu of pulse(); what is the difference in this circumstance? it still is confusing to me between pulseall and pulse

    @Ace... Same quote- until now I had never seen that before. I am looking into it as well, but I don't know for my application that it is a best fit, but I will explore it.

    I code C hash-tag .Net
    Reference: W3C W3CWiki .Net Lib
    Validate: html CSS
    Debug: Chrome FireFox IE

  • #12
    New Coder
    Join Date
    Aug 2013
    Posts
    29
    Thanks
    1
    Thanked 6 Times in 6 Posts
    I think this article explains C# threading constructs very well: Threading in C# - Free E-book

    It explains the monitor pattern as well as higher level constructs like tasks.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •