1

I have a program that monitor a DB (or a number of DBs). for this, I built a class that holds all the information about how to monitor the DB. the class contains a delegate that points to a function that monitor the DB and changes the state field accordingly.

the main thread create a new instance of the class and calling the class.delegate.begininvoke(). the main thread checks the state of each created class in a loop and inform the user if any changes occur.

a simple example of the code:

Class Monitor
{
    private Object lockObj;
    private delegate void MonitorHandlerDelegate();
    private MonitorHandlerDelegate mainHandler;

    private int State;
    private int DBid;

    public Monitor(int DBid)
    {
        this.DBid = DBid;
        mainHandler += MonitorHandler;
        lockObj = new Object();
    }

    private void MonitorHandler()
    {
        do
        {
            state = CheckDB(DBid); // 0 is ok, 1 is fail, 2 is InWork, 3 is stop monitoring
        } while (state != 3);
    }

    public int state
    {
        get { lock(lockObj) { return State;} }
        set { lock(lockObj) {State = value;} }
    }

    public void Start()
    {
        this.state = 0;
        this.mainHandler.BeginInvoke(null, null);
    }
}

public Main()
{
    Monitor firstMonitor = new Monitor(20);
    firstMonitor.Start();
    do
    {
        if(firstMonitor.state == 1) WriteLine("DB 20 stop working");
    } while(true);
}

The problem I encountered is with exception handaling, if the MonitorHandler function throw an exception, i dont have a way to know it.

I dont call the EndInvoke so the exception is not re-throwing to the Main Thread.

My goal is to check the DB status by simply chack the state field of the monitor instance. If an exception in throwen i need to somehow "transfer" this exception to the Main Thread but i dont want to start checking the state and the Monitor delegate status as well.

I whold love to find a way to the Monitor Thread itself (the one that activated by the .BeginInvoke), to throw the exception in the Main Thread.

Thank you.

Yakir Shlezinger
  • 147
  • 1
  • 13
  • 1
    What you've essentially re-implemented here is polling, not once but twice, and in the most inefficient way possible. Have you checked your database to see if it already has a way to do this? See also [What is the X Y Problem?](https://meta.stackexchange.com/a/66378) – Robert Harvey Jan 20 '20 at 19:32
  • If you use `delegate.BeginInvoke` you **must** call `EndInvoke` or your delegate will never be disposed. If you want to get results, including exceptions, from a delegate without disposing it you should use a [completion callback](https://stackoverflow.com/questions/1746332/). – Dour High Arch Jan 20 '20 at 19:32
  • Robert Harvey this is an example of the probleom i am trying to solve. i don't really check the DB itself but the data that in the DB, the CheckDB function takes data from the DB and checks it. If the Data is outside a predifned limits (or some other check on the data) an Alarm state is raised. i want the main thread to check when an alarm state is raised and act accordingly. – Yakir Shlezinger Jan 20 '20 at 19:40
  • Dour High Arch, correct me if i am mistaken but the the delegate is not disposed when the function is over? i.e when state == 3 ? – Yakir Shlezinger Jan 20 '20 at 19:41
  • No, the delegate is not disposed when the function is over because it is global to your class, not local to the method. Look at your class, `private MonitorHandlerDelegate mainHandler` is a class instance, not a local one. Yes, if your delegate has no external state or resources then [not disposing it won't leak resources](https://stackoverflow.com/questions/4585042/), but that is incredibly risky and [you should always call `EndInvoke`](http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx). – Dour High Arch Jan 20 '20 at 19:54

1 Answers1

2

I whold love to find a way to the Monitor Thread itself (the one that activated by the .BeginInvoke), to throw the exception in the Main Thread.

Other than something like ThreadAbortException, there is no mechanism to inject an exception into another arbitrary thread.

If you are going to use the delegate's BeginInvoke() method, and you want to catch the exception in a thread different from where the delegate itself is being invoked, then you will need to call EndInvoke() in that thread.

Your other option would be to deal with the exception explicitly and manually. I.e. catch the exception with try/catch in the worker thread, and then use an explicitly defined mechanism of your own choosing (e.g. a ConcurrentQueue<T>) to pass the caught exception to code running in the main thread.

All that said, using a delegate's BeginInvoke() method was never really that ideal a way to execute code asynchronously like that, and today it is even worse of an idea. It's not clear from your question what the nature of the "main thread" is, never mind whether that thread has a synchronization context. But assuming it does (e.g. it's a GUI thread, or an ASP.NET context, etc.) then your desired behavior is easily implemented using Task.Run() to start the asynchronous operation, and then using await in the main thread to capture the completion of that operation, along with any exception that is thrown.

For that matter, even if your main thread does not currently have a synchronization context, it might be the right approach is to give it one. Either by leveraging one of the existing mechanisms, or writing your own. This would be a good idea if, for example, you expect to run into this sort of "propagate the exception from the worker to the main thread" scenario in the code frequently. This would allow you to use the built-in language support for dealing with that (i.e. async/await), rather than having to kludge something for each instance. Implementing the synchronization context isn't trivial, but it's work you can do once, and then reuse over and over.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136