Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Prevent Thread Re-entrance on Same Thread 2

Status
Not open for further replies.

sammye

Programmer
Oct 5, 2007
35
US
I am looking for a way to prevent reentrance on the same thread. The problem is that I have a static function that is called from a form in a couple of ways (button, timer), and I have been using a lock to prevent reentrance from other threads and that portion seems to be working fine. However, I believe that asynchronous events on the same thread are causing me trouble.

Any suggestions? Is this a good place for a semaphore? I am not very familiar with using semaphores, mutex's, etc, so I am looking for a pointer in the right direction. All I know is that lock doesn't cover what I need. Thanks.

Joe
 
do you really need the singleton? either have the singleton return a new instance of the object to do the work, or don't use a singleton at all.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Sorry, but I'm a little confused as to where you got the singleton from. I have some idea of how a singleton works, but will it help in my case?

I have a function which I want to prevent re-entrance from multiple threads and also the same thread. The lock model works across threads, but not within the same thread.

In my case, I have a main form with a button call to the function as well as a form timer which happens to call it as well. I want to prevent the case of having the button call interrupt the timer call or vice versa. There is also another form on another thread making calls to the same function, but the lock takes care of that case.
 
sorry, static, not singleton. make the static function an instance function.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
If changed to a function belonging to an instance object, what's the best way of allowing access to multiple threads? Won't it still have the same issue of the being re-entered on the same thread with asynchronous events?

Out of curiosity, can you tell me if my method of sharing data is totally bogus or not? One model I've tried is having a single update loop that reads data and writes it into a static class, with multiple readers roaming around accessing the static class. I know that writes to int's, doubles, etc. are not volatile operations so I've wrapped those properties with lock statements to protect against multiple threads accessing at the same time.

I'm hoping that something like the Interlocked.CompareExchange mentioned in the link you sent will handle events on the same thread, along with of course dealing with multiple threads.

What do you think? Decent approach or a steaming pile of...?


joe
 
Instead of the lock() statement, look into using the System.Threading.Mutex object.

Note that the Mutex object is a wrapper around the Win32 mutex structure, and that it's really the nuclear option in terms of resource protection -- do it wrong, and your whole system could slow down.

Chip H.


____________________________________________________________________
www.chipholland.com
 
I'm questioning the need for static/singleton objects begin shared across threads. Usually they are not needed. If you do use a singleton/static member then the object in questions should not have state, only behavior.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Jason:

Please question away, I'd definitely appreciate the advice. I've heard that static classes/members should be restricted to functions that do not carry state, but it also seems like a very straight forward way of sharing data across multiple threads.

Is there a better way of doing this? Even if a better way, is there a way to make static classes acting as data holders work as there is quite a bit of momentum behind it.

Chip:
I'll doing a little reading on mutex's, thanks for the warning on it though.


joe
 
I've heard that static classes/members should be restricted to functions that do not carry state
I would agree that this is a good guideline, but I wouldn't make it static, just because it doesn't carry state.

My approach is to build all class members as instances. I use an IoC container to manage the scope of those the object. Now the responsibility of (single, instance, instance per thread, etc) is the IoC, not the class itself.

but it also seems like a very straight forward way of sharing data across multiple threads.
why do you need to share data across threads?

is there a way to make static classes acting as data holders work as there is quite a bit of momentum behind it.
what do you mean by momentum?

Here is another way to approach the problem. What are the consequences to you application if you remove the static operator and use a new instance of the object on each thread?

My understanding is that a thread makes for a good context boundary. where all work done within the thread is atonomous, without any regard for work done on other threads. the results of this work can then be returned to the calling thread.

I re-read the initial post and I am confused about one thing. Are you currently experiencing a problem, or are you anticipating one? If you are having a problem, what is the exact issue. If an exception is thrown what are the details; type, message, stack trace?

If there is not a problem yet this discussion may be mute.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Good questions/comments. The reason I need to share info across states is that I have few different hardware resources attached to a pc (usb and pci). There are two threads which update the UI so the user can see what is going on and then there is also a thread which does the actual work and needs to interact with the hardware resources.

For one resource (pci) which I am getting occasional errors from, I created a static class that reads/writes to it and does not hold any state info. The error is below. I am not certain that the issue is due to a threading issue, but it is a suspicion that I am working through.

"System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

The error is inconsistently reproducible unfortunately so is difficult to troubleshoot, but it still pops up. The extra wonderful thing about it is that it never occurs while running in debug, but plenty when running the released app.

Make sense?
 
sammye said:
The error is inconsistently reproducible unfortunately so is difficult to troubleshoot, but it still pops up. The extra wonderful thing about it is that it never occurs while running in debug, but plenty when running the released app.

I love race conditions...

/Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
The reason I need to share info across states is that I have few different hardware resources attached to a pc (usb and pci). There are two threads which update the UI so the user can see what is going on and then there is also a thread which does the actual work and needs to interact with the hardware resources.
this sounds like a good scenario to implement the Observer Pattern. .Net handles this with events.

I'll see if I cannot put something together to demonstrate this.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
By the way, there are two different scenarios being used. One is the case I mentioned above with a static class and a bunch of static methods. The 2nd uses a static reference to an instantiated object. Both are causing me trouble, but it is actually the 2nd that produces the protected memory error I mentioned above. Thanks again.
 
Ok, I'm working on a example using a timer to simulate new information arrived. the following exception is thrown
Code:
System.InvalidOperationException was unhandled by user code
  Message="Cross-thread operation not valid: Control 'History' accessed from a thread other than the thread it was created on."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.Control.set_WindowText(String value)
       at System.Windows.Forms.Control.set_Text(String value)
       at System.Windows.Forms.Label.set_Text(String value)
       at ObserverSample.SecondUserControl.DisplayHistoryOfStats(Object sender, PollingEventArgs args) in C:\jmeckley\Visual Studio 2008\Projects\observer.pattern\src\ObserverSample\SecondUserControl.cs:line 21
       at ObserverSample.model.PollingEvent.Invoke(Object sender, PollingEventArgs args)
       at ObserverSample.model.PollingService.onStatisticsPolled(Object sender, ElapsedEventArgs e) in C:\jmeckley\Visual Studio 2008\Projects\observer.pattern\src\ObserverSample\model\PollingService.cs:line 26
       at System.Timers.Timer.MyTimerCallback(Object state)
  InnerException:
This sort of makes sense to me. The thread used by the timer is not the main thread. When i attempt to update the GUI I get this exception because the GUI is not on the same thread as the timer.

MS Help sent me here. But I'm not crazy about this exact method because it mean duplicated code throughout my presentation layer.

I'll post what I have so far maybe another member of the forum can help us.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Code:
using System;
using System.Windows.Forms;
using ObserverSample.model;
using Timer=System.Timers.Timer;

namespace ObserverSample
{
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        private static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(CreateContext());
        }

        private static ApplicationContext CreateContext()
        {
            const int twoSeconds = 2000;
            var timer = new Timer(twoSeconds);
            var pollingService = new PollingService(timer);
            timer.Start();

            var context = new ApplicationContext();
            context.MainForm = new Form1(pollingService);
            context.ThreadExit += (sender, args) =>
                                      {
                                          pollingService.UnregisterTimer(timer);
                                          timer.Dispose();
                                      };
            return context;
        }
    }
}
Code:
using System;
using System.Windows.Forms;
using ObserverSample.model;

namespace ObserverSample
{
    public partial class Form1 : Form
    {
        private readonly IPollingService Service;

        public Form1(IPollingService service)
        {
            Service = service;
            InitializeComponent();

            Disposed += StopDisplayingStatistics;
        }

        private void StopDisplayingStatistics(object sender, EventArgs e)
        {
            Service.StatisticsPolled -= firstUserControl1.DisplayCurrentStats;
            Service.StatisticsPolled -= secondUserControl1.DisplayHistoryOfStats;
            Disposed -= StopDisplayingStatistics;
        }

        protected override void OnLoad(EventArgs e)
        {
            Service.StatisticsPolled += firstUserControl1.DisplayCurrentStats;
            Service.StatisticsPolled += secondUserControl1.DisplayHistoryOfStats;
        }
    }
}
Code:
using System.Windows.Forms;
using ObserverSample.model;

namespace ObserverSample
{
    public partial class FirstUserControl : UserControl
    {
        public FirstUserControl()
        {
            InitializeComponent();
        }

        public void DisplayCurrentStats(object sender, PollingEventArgs args)
        {
            var statistics = args.Statisics;
            var message = string.Format("{0} Iteration {1:000} took place at {2: MM-dd-yy HH:mm:ss}", statistics.Text, statistics.Number, statistics.OccuredAt);
            CurrentStats.Text = message;
        }
    }
}
Code:
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using ObserverSample.model;

namespace ObserverSample
{
    public partial class SecondUserControl : UserControl
    {
        private readonly List<PollingStatistics> HistoryOfStatistics;

        public SecondUserControl()
        {
            HistoryOfStatistics = new List<PollingStatistics>();
            InitializeComponent();
        }

        public void DisplayHistoryOfStats(object sender, PollingEventArgs args)
        {
            HistoryOfStatistics.Add(args.Statisics);
            History.Text = BuildMessage();
        }

        private string BuildMessage()
        {
            var builder = new StringBuilder();
            HistoryOfStatistics.ForEach(stat => builder.AppendLine(string.Format("{0} Iteration {1:000} took place at {2: MM-dd-yy HH:mm:ss}", stat.Text, stat.Number, stat.OccuredAt)));
            return builder.ToString();
        }
    }
}
Code:
using System;
using System.Timers;

namespace ObserverSample.model
{
    public interface IPollingService
    {
        event PollingEvent StatisticsPolled;
    }

    public class PollingService : IPollingService
    {
        private int Number;

        public PollingService(Timer timer)
        {
            //null polling event so it's never null;
            Number = 1;
            StatisticsPolled += (sender, args) => { };

            timer.Elapsed += onStatisticsPolled;
        }

        private void onStatisticsPolled(object sender, ElapsedEventArgs e)
        {
            StatisticsPolled(this, new PollingEventArgs(new PollingStatistics("hello world", Number++, e.SignalTime)));
        }

        public event PollingEvent StatisticsPolled;

        public void UnregisterTimer(Timer timer)
        {
            timer.Elapsed -= onStatisticsPolled;
        }
    }

    public delegate void PollingEvent(object sender, PollingEventArgs args);

    public class PollingEventArgs : EventArgs
    {
        public PollingStatistics Statisics { get; private set; }

        public PollingEventArgs(PollingStatistics statisics)
        {
            Statisics = statisics;
        }
    }

    public class PollingStatistics
    {
        public string Text { get; private set; }
        public int Number { get; private set; }
        public DateTime OccuredAt { get; private set; }

        public PollingStatistics(string text, int number, DateTime occuredAt)
        {
            Text = text;
            Number = number;
            OccuredAt = occuredAt;
        }
    }
}

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Whoa, there's a lot going on in that sample. Thanks for being so thorough. I'll begin reading through it and try to understand it.

However, I am curious if you can tell me what the problem with the below scenario:

Thread 1: Read from hardware resource and place results in static hash table
Thread 2: Read and display values from static hash table
Thread ...N: Read and display values from static hash table
(The reader threads could even get a copy of the hash table if that helps)

Per MS, hash tables support multiple readers as long as there is only 1 writer:
"A Hashtable can support one writer and multiple readers concurrently."

I'm asking this because of the "momentum" I mentioned earlier, which I forgot to explain. There's quite a bit of code that would have to be changed in order to follow a model like what you detailed out. Of course I don't mind doing it if it is the right way, but I am *hoping* that the method I described does in fact work as it seems to make sense to me. Thanks for the hard work.
 
Honestly, I'm not sure the benefits of a synchronized hashtable.

some questions i would ask are:
why a static hashtable? from my point of view you keep pushing static objects rather than instances.
why so many threads? couldn't all the work be pushed to 1 background thread processed. when the results are returned from the background update each UI component.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Sorry, the synchronized hashtable is for the case of handling multiple writers. The doc's state that a normal hashtable can handle multiple readers with a single writer just fine.

I'm stuck on static objects because of two reasons:
1. Ease of use. All I have to do to get data out of the static hashtable from any thread is access it:
myLocalVar = MyStaticClass.staticHashInstance["varName"]

2. Because it's already implemented =P (yes, kinda lame excuse, but I'm dealing with a lot of legacy code)

While I see how the example you provided would work terrifically, I am still unsure as to why what's already there wouldn't work. Any thoughts here???

Lastly, the reason there aren't just the UI and background threads is because some of the connected hardware is very slow. To make use of the multiple cpu's and the extra clock cycles, there are threads managing individual pieces of hardware in some cases.

I really appreciate the questions/comments, they're forcing me to rethink some areas. Any chance you're in the Seattle area and I can buy you a beer =P?


Joe
 
While I see how the example you provided would work terrifically, I am still unsure as to why what's already there wouldn't work. Any thoughts here???
nope, I'm stuck there as well.
Any chance you're in the Seattle area and I can buy you a beer =P?
Thanks for the beer, but I'm on the east cost, Pennsylvania.

At this point I would step back and re-evaluate my approach. This might even include creating a new project just to test querying the hardware for stats and printing to a console.

another option:
have a service query the hardware at regular intervals. store the stats in a dictionary. then have a timer on the form fire updates to query the dictionary of stats. this would give you near-real-time results.

I would anticipate you still have locking concerns with reading/writing to the dictionary, and maybe even a timer on the form, but it's worth a shot.

one final note: Hashtables are great, as long as they are encapsulated. Nothing is more fragile than accessing a public hashtable. there are no compile time checks, or strongly typed accessors.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top