?
Solved

c# multithreading and global variables

Posted on 2012-09-04
12
Medium Priority
?
1,614 Views
Last Modified: 2012-09-05
While one of my past questions enables my c# program to multithread as seen here:
http://www.experts-exchange.com/Programming/Languages/.NET/Q_27820091.html

The problem i am now running into is I have routines that include global variables that were basically designed for as if I am not using multithreading, so I was wondering before I make any drastic decisions whether it is wiser to make my global variables as arrays so it can accomodate for up to say 50 concurrent transaction or perhaps spawn as a new c# program or service the performs each task on given file request.

As left off, here's my main loop which seeks and processes any request files that come along:

            WaitCallback wcbdoRequestFile = new WaitCallback(doRequestFileO);
            while (true)
            {
                string[] files = System.IO.Directory.GetFiles(GlobalVars.REQpath, "*.req", SearchOption.TopDirectoryOnly);
//              acquire all *.req files in folder
                for (int fcnt = 0; fcnt < files.Length; fcnt++)  
                {
                    string rqfile = files[fcnt].Replace(".req", ".re0");
                    System.IO.File.Delete(rqfile);
                    System.IO.File.Copy(files[fcnt], rqfile);
                    System.IO.File.Delete(files[fcnt]);

                    //doRequestFile(rqfile);
                    ThreadPool.QueueUserWorkItem(wcbdoRequestFile, rqfile);
                }
                Thread.Sleep(5000);  // the pause is to purposely prevent any IOException errors
            }

Here are some of my global variables involved which essentially involves creating an xml file and holding array of variables for processing:
            public static XmlDocument xmlout;
            public static XmlNode xmldnde;
            public static XmlElement xmlelem1;
            public static XmlElement xmlelem2;
            public static XmlElement xmlelem3;
            public static XmlElement xmlelem4;
            public static XmlElement xmlelem5;
            public static XmlText xmltext;

          public static int[] xmlColcnt  
            public static string[] aMSGNbr
0
Comment
Question by:accucom
  • 6
  • 6
12 Comments
 
LVL 19

Expert Comment

by:Ken Butters
ID: 38364947
Is there any reason that they have to be static?  Seems to me that trying to make an array, so that you can hold one for each thread completely overrides the need for static.

Just make make them public, and it should create a separate instance for each thread.
0
 

Author Comment

by:accucom
ID: 38365072
When I remove the static portion, I start seeing the following example in my error list:

Error      5      An object reference is required for the non-static field, method, or property 'collect4.collect4.GlobalVars.xmlout'      C:\Accucom\collect4\collect4.cs      290      17      collect4

GlobalVars.xmlout.ChildNodes.Item(1).AppendChild(GlobalVars.xmlelem2);

Error      50      An object reference is required for the non-static field, method, or property 'collect4.collect4.GlobalVars.xmlelem3'      C:\Accucom\collect4\collect4.cs      1006      17      collect4

GlobalVars.xmlelem3 = GlobalVars.xmlout.CreateElement("", "ROW", "");


I'm hoping there's still another way of doing something similar to the following without using static and making this work through multithreading:
            static string _REQfn;
            public static string REQfn
            {
                get
                {   return _REQfn;                }
                set
                {   _REQfn = value;                }
            }
0
 
LVL 19

Expert Comment

by:Ken Butters
ID: 38365128
When the variable is not static.... it must be associated with / part of a particular instance of a class.

you need to do create a new instance of the class... and then refer to the variables that exist in that instance.

That is what the error " an object reference is needed" means.

If you make the item static... like this:
 
static string _REQfn;
            public static string REQfn
            {
                get
                {   return _REQfn;                }
                set
                {   _REQfn = value;                }
            } 

Open in new window


Then it is not thread-safe.  becauase any thread at any time could set and or retrieve a value overwritten by another thread.

instead... you need to make them non-static... but instead... make them a part of whatever class you are using them in.
0
Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

 

Author Comment

by:accucom
ID: 38365151
Attached is the snippet of my code that I'm trying to make non-static if possible so I can make my multithreading work as desired.  I'm open to suggestions how to do this differently.
globalvars.txt
0
 
LVL 19

Accepted Solution

by:
Ken Butters earned 2000 total points
ID: 38365242
My suggestion for this class... is to remove static from every single declaration within the class... including the class itself.


Then each thread should create a new instance of the GlobalVars class.

GlobalVars myGVARS = new GlobalVars();

then reference all your fields from myGVARS (or whatever you name your specific instance).

I am assuming that you need separate instances of this information for each thread... and that you don't need to share any information between threads.

see example attached.
globalvars.txt
0
 

Author Comment

by:accucom
ID: 38365314
So if I understand correctly, it appears should create a new instance of this class at the beginning of each thread.

GlobalVars myGVARS = new GlobalVars();

example calls include:

doFmpXMLResult(node);
static void doFmpXMLResult(XmlNode node)

MainReqLoop();
static void MainReqLoop()

make it?:

doFmpXMLResult(node, myGVARS);
static void doFmpXMLResult(XmlNode node, GlobalVars myGVARS)

MainReqLoop(myGVARS);
static void MainReqLoop(GlobalVars myGVARS)

Will I need to pass it in all my associated thread calls as well?  I probably should isolate the variables that will indeed be separate per thread as I notice some of these variable will indeed be consistent for all threads at least.
0
 
LVL 19

Expert Comment

by:Ken Butters
ID: 38365459
Yes... I think you pretty much have it.

The static should be reserved only for thost things that do not depend on a separate instance of a class.

I'm not sure what information each individual thread needs... but I gather it needs to know a filenumber.... so that it contains a unique number to name the file it is processing?  Not sure if I have that right or not...

But supposing I do... then maybe your main loop function is the only one that needs to keep track of a file number and then pass the most recent file number on to a given worker thread.

But yes... you really do need to separate out those items that need to remain constant / shared for all threads vs... those that are unique to each thread.

Also... keep in mind that if a static variable is shared/accessed by more than one thread.... you really shouldn't change it's value if you want to remain thread-safe.   You can get around this by locking.... but I think it's better to avoid this if at all possible.

Lock: http://msdn.microsoft.com/en-us/library/c5kehkcz%28v=VS.100%29.aspx
0
 

Author Comment

by:accucom
ID: 38365596
I'm optimistic this shall work as I'll have both a GlobalVars & a ThreadVars section where desired.  The filenames are based on usernames:

ie.
aciadmin.req
hcd1111.req
hcd1122.req

The one other problem I was also running into was where I write to a common log file which I want to share with all threads where I get an exception error for it already being in use:

        static void logWriteLine(string s)
        {
            //Console.WriteLine(s);
            string logfile = GlobalVars.LOGpath + "collect4_" + DateTime.Now.ToString("yyyyMMdd") + ".log";
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@logfile, true))
            {
                file.WriteLine(s);
            }  

        }
0
 
LVL 19

Expert Comment

by:Ken Butters
ID: 38365711
from this article :

http://stackoverflow.com/questions/1344025/how-to-make-a-class-thread-safe

public class SharedLogger : ILogger 
{   
     public static SharedLogger Instance = new SharedLogger();    
     public void Write(string s)    
     {       
          lock (_lock)       
          {          
                  _writer.Write(s);       
           }    
      }     
      private SharedLogger()     
      {        
            _writer = new LogWriter();    
      }     
      private object _lock;    
      private LogWriter _writer; 
} 

Open in new window


this example uses the Lock to keep only one thread writing to log file at a time.
0
 

Author Closing Comment

by:accucom
ID: 38369376
After making tedious changes throughout my program, I was successfully able to process 2 identical files under different names and did a file compare on the outputs to make sure they were identical.  While I didn't make any changes to the log writing procedure, I wonder if my being able to isolate my global variables enough did the trick or was just luck my 2 didn't concurrently bumped heads.  

So far as the log locking example, I didn't quite understand it though if such does occur then how about doing a while loop until no errors or something to that effect.
0
 

Author Comment

by:accucom
ID: 38369404
It looks like I'll still have to deal with the "The process cannot access the file 'C:\Accucom\collect4\logs\collect4_20120905.log' because it is being used by another process." message when my program is running in service mode as hopefully there's a way I can handle this so I can truly do my concurrent processing without keeping my program open in debug mode.
0
 
LVL 19

Expert Comment

by:Ken Butters
ID: 38369426
the log writing should be locked....I think you just got lucky.

What the lock does in effect is lock down the execution of a particular piece of code, so that only one thread is in that piece of code at a time.... (sort of like locking the bathroom door so nobody else comes in while your in there) :)

What the sample code above does... is lock the actual Log write method.... so that only one thread attempts to write at single instance in time.

As soon as one thread leaves that code.... then another thread is allowed into it.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Recently while returning home from work my wife (another .NET developer) was murmuring something. On further poking she said that she has been assigned a task where she has to serialize and deserialize objects and she is afraid of serialization. Wha…
This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses

829 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