3

Would anyone care to explain to me how the value of this.oBalance.QouteBalance is evaluated to be true for being less than zero when it clearly isn't? Please see image below.

Am I missing something fundamental when it comes to comparing doubles in C#??

enter image description here

public double QouteBalance { get; set; }

UpdateBalance_PositionOpenned() is not being called in a loop, but is being called as part of a more complex event driven procedure that runs on the ticks of a timer (order of milliseconds)

EDIT: Pardon the code if it's messy but I couldn't edit it as this was a run-time error after quite a long run-time so was afraid wouldn't be able to recreate it. The Exception message is not correct and just a reminder for myself. The code after the exception is code I forgot to comment out before starting this particular run.

EDIT 2: I am building and running in Release Mode.

EDIT 3: Pardon my ignorance, but it would seem that I am in fact running in a multi-threaded environment since this code is being called as part of a more complex object method that gets executed on the ticks (Events) of a timer. Would it possible to ask the timer to wait until all code inside its event handler has finished before it can tick again?

EDIT 4: Since this has been established to be a multi-threading issue; I will try to give wider context to arrive at an optimized solution.

I have a Timer object, which executes the following on every tick:

  1. Run a background worker to read data from file
  2. When background worker finishes reading data from file, raise an Event
  3. In the event handler, run object code that calls the method below (in the image) and other multiple routines, including GUI updates.

I suppose this problem can be avoided by using the timer Tick events to read the from file but changing this will break other parts of my code.

rex
  • 3,133
  • 6
  • 35
  • 62
  • 6
    Multi-threading? – abatishchev Mar 04 '14 at 20:15
  • 1
    If you step over the current line, is the exception actually thrown? If not, your symbols are probably out of date, try rebuilding. – Lee Mar 04 '14 at 20:16
  • 3
    Note: It's a little odd to try and execute a line of code **after** throwing an exception. With my current week. I'd never trust the Visual Studio Debugger to give an accurate value. Try outputting it somewhere. PS - you don't show us the definition of `QouteBalance` anywhere that I'm seeing. Certainly `0` is not a double in this case... –  Mar 04 '14 at 20:16
  • @abatishchev, I do not modify QouteBalance in any other class or thread. – rex Mar 04 '14 at 20:20
  • @Lee, Can you please elaborate as to what this means and how it comes about? – rex Mar 04 '14 at 20:20
  • @ebyrob, Thanks, I forgot to comment line after throw out; should't be there. QouteBalance is a double, could the error be because I'm not using 0.0? – rex Mar 04 '14 at 20:21
  • technically, the exception is wrong since you're checking for a balance less than zero! This could be a case of implicit conversion. Try doing `this.oBalance.QouteBalance < 0M`. The `M` signifies that the zero should be treated as a decimal. Other than that, post the `QouteBalance` property's code. – ps2goat Mar 04 '14 at 20:22
  • 1
    @ArmenSafieh-Garabedian Lee means that your source code and your binaries very likely don't match up. `0.0` is worth a shot. More importantly, I'd use a print statement instead of debugger to get that value. (especially in a case like this, you could be in release with an optimization hiding something) –  Mar 04 '14 at 20:22
  • I know this isn't an answer to your current issue, but you'll want to replace `< 0` with `<= 0` since the error says it can't be zero. – TyCobb Mar 04 '14 at 20:23
  • 1
    @TyCobb How can you assume which is wrong? The message could be wrong. The code could be wrong. Changing either could make it twice as worse. –  Mar 04 '14 at 20:24
  • Do you have anything in your watch window that causes debugger to do an evaluation and thus cause values to change. I ran into this a few times where I left something in watch window, and that affected how program ran when debugged vs non-debugged runs. – LB2 Mar 04 '14 at 20:25
  • @ebyrob I don't, but just trying to help because it's what I noticed and typing out error messages usually indicate the actual logic that is to be achieved and `<` vs `<=` is an easy miss. Again, was just trying to help.... – TyCobb Mar 04 '14 at 20:28
  • @TyCobb, Thanks, good spot, but message isn't that relevant to me, just giving me a hint of the problem. Please see update. – rex Mar 04 '14 at 20:28
  • 2
    @ArmenSafieh-Garabedian: If you are representing money, please use `decimal` instead of `double`: http://stackoverflow.com/questions/693372/what-is-the-best-data-type-to-use-for-money-in-c – myermian Mar 04 '14 at 20:31
  • @LB2 Sorry not so good with VS2013 so don't know what the Watch Window is, would rebuilding fix this issue? I rebuild the code every time I run it anyway. – rex Mar 04 '14 at 20:31
  • @ArmenSafieh-Garabedian Watch window is one of the debugger window into which you can put an expression and it will evaluate it every time you hit a break point or step thru the code. An issue can arise if you have an expression there that alters state of the program and thus makes values appear odd / modify how program runs as you step through it. – LB2 Mar 04 '14 at 20:34
  • Are you calling `UpdateBalance_PositionOpenned` directly or is it registered as an event handler of some sort? – Erik Noren Mar 04 '14 at 20:36
  • @LB2 Thanks, I am not using Watch Windows and opened up all 4 now to check if something was there by accident but they are empty. – rex Mar 04 '14 at 20:37
  • @ErikNoren, It's being called directly by the class code. – rex Mar 04 '14 at 20:38
  • How is it called? In a loop? What's the calling logic - that might point to the issue since this segment of code doesn't appear to be the issue as-is. We need a bit more context I think. – Erik Noren Mar 04 '14 at 20:41
  • @ArmenSafieh-Garabedian Put `Debug.Print` just before and just after the if statement, and write out the value both times. then let it run in debugger but without stepping through. what values will it be printing? – LB2 Mar 04 '14 at 20:41
  • Just a last thought... kind of a fire and forget. Put the "error value" of `QouteBalance` in the exception message (unless of course that's a security problem). Then if you have to wait for another iteration and long run cycle, you'll find out more next time. (Proper discipline here is where exceptions go from "ok tool" to "amazingly effective and indispensible") –  Mar 04 '14 at 20:41
  • Sometimes this sort of "inexplicable bug" results from debugging a different version of the code. eg, the DLL being used was built and then you edited the code, or something like that. If you can delete all the code and rebuild from scratch, then try to repro. – Brian Reischl Mar 04 '14 at 20:44
  • @ebyrob, Thanks, I will do this. – rex Mar 04 '14 at 20:44
  • 3
    Given your latest update, you lied to us! This is multithreaded. Events are fired on different threads. It's probably a simple race condition since you're accessing shared variables from multiple threads. – Erik Noren Mar 04 '14 at 20:45
  • Release builds are highly unreliable for debugging - even without threading issues, the debugger frequently shows rubbish values when debugging. Try a debug build. – Jason Williams Mar 04 '14 at 20:48

3 Answers3

2

You're accessing shared variables from multiple threads. It's probably a race condition where one thread has thrown the error but by the time the debugger has caught and attached, the variable's value has changed.

You would need to look at implementing synchronizing logic like locking around the shared variables, etc.

Edit: To answer your edit:

You can't really tell the timer to not tick (well you can, but then you're starting and stopping and even after calling Stop you might still receive a few more events depending on how fast they are being dispatched). That said, you could look at Interlocked namespace and use it to set and clear and IsBusy flag. If your tick method fires and sees you're already working, it just sits out that round and waits for a future tick to handle work. I wouldn't say it's a great paradigm but it's an option.

The reason I specify using the Interlocked class versus just using a shared variable against comes down to the fact you're access from multiple threads at once. If you're not using Interlocked, you could get two ticks both checking the value and getting an answer they can proceed before they've flipped the flag to keep others out. You'd hit the same problem.

The more traditional way of synchronizing access to shared data member is with locking but you'll quickly run into problems with the tick events firing too quickly and they'll start to back up on you.

Edit 2: To answer your question about an approach to synchronizing the data with shared variables on multiple threads, it really depends on what you're doing specifically. We have a very small window into what your application is doing so I'm going to piece this together from all the comments and answers in hopes it will inform your design choice.

What follows is pseudo-code. This is based on a question you asked which suggests you don't need to do work on every tick. The tick itself isn't important, it just needs to keep coming in. Based on that premise, we can use a flagging system to check if you're busy.

...
Timer.Start(Handle_Tick)
...

public void Handle_Tick(...)
{
    //Check to see if we're already busy. We don't need to "pump" the work if
    //we're already processing.
    if (IsBusy)
        return;

    try
    {
        IsBusy = true;

        //Perform your work
    }
    finally
    {
        IsBusy = false;
    }
}

In this case, IsBusy could be a volatile bool, it could be accessed with Interlocked namespace methods, it could be a locking, etc. What you choose is up to you.

If this premise is incorrect and you do in fact have to do work with every tick of the timer, this won't work for you. You're throwing away ticks that come in when you're busy. You'd need to implement a synchronized queue if you wanted to keep hold of every tick that came in. If your frequency is high, you'll have to be careful as you'll eventually overflow.

Erik Noren
  • 4,279
  • 1
  • 23
  • 29
  • Edited to respond to your latest edit. You can't tell timer to skip ticks but you can use a shared flag to indicate you want to ignore a tick or two while you catch up. It's not great code but it's an option. – Erik Noren Mar 04 '14 at 20:55
  • Would you say that I am more than likely not to have this issue if my ticks were slower than the time it takes to execute all the code in the event handler? – rex Mar 04 '14 at 20:58
  • @ArmenSafieh-Garabedian It would have to be "quite a lot slower". Even then, it would be nothing even remotely close to a guarantee because of the way most GUI processes (especially GC'ed ones) tend to "hiccup". –  Mar 04 '14 at 21:00
  • You would be less likely to hit them but they can still happen. Events don't always fire immediately on tick. There are a number of things that could cause your event handler to be delayed in its execution. If one tick gets delayed and another comes in, you'd have the same problem. Now if it takes a couple millseconds to execute your code and the ticks are 1 minute apart, it's such a low likelihood you'd probably never need to worry. But I need to stress this is still not good! It depends on how much your data integrity matters. – Erik Noren Mar 04 '14 at 21:00
  • It's a question of risk/reward. If this is data that must be 100% accurate, you need to spend extra time implementing good locking/synchronization. If a mistake here or there aren't a problem, you can space things out and hope for the best and deal with the exceptions if they happen and use that saved time for something else. Is this code that will for months or years? Or is it a one-time import/conversion? – Erik Noren Mar 04 '14 at 21:03
  • Thanks guys, data integrity is critical, so apart from replacing all my doubles with decimals (oopsie), I need to look into locking the variables for multi-threaded execuation. Would sample code be short and a potential addition to your post @ErikNoren? :D – rex Mar 04 '14 at 21:04
  • It wouldn't be helpful without knowing more about your application. I would use a lock around the shared variables but it sounds like they might be getting touched in other areas not to mention a high frequency of events will cause things to gum up. I don't think any simple example will be helpful to your situation. If you have some pseudo code, that could help. – Erik Noren Mar 04 '14 at 21:07
  • 1
    @ErikNoren I think I'd be tempted to use `Monitor.TryEnter()` with a very very short wait timeout. If I didn't get the lock, I'd just exit the highest level event (from timer) at that point. (Further, once I got the lock, I could check it'd been long enough since last start/stop times that I should do something that won't bog the system) If `TryEnter(foo, 1)` was too slow, then I'd consider a controlling thread of my own instead of a timer perhaps... –  Mar 04 '14 at 21:07
  • @ebyrob That's actually an interesting suggestion. It would basically be like the flagging system I initially suggested (for skipping ticks until you're ready to work again). I don't often jump to semaphores and their ilk right away so I tend not to come up with them as answers unless I'm writing the code myself. :) – Erik Noren Mar 04 '14 at 21:09
  • @ErikNoren It just feels like a problem where `lock` is needed, and I've been slowly learning if you need `lock`, do it once and do it right. Don't try to sometimes skip it or delay it cause you'll likely break your application in esoteric ways. –  Mar 04 '14 at 21:16
  • Thinking about this, I think skipping ticks would be a problem for me as I DO NOT want the rest of the code in the program to execute until this has finished. The ideal solution would be to make the Timer wait until its event handler has finished executing, at the expense of performance - which is something I also care about. – rex Mar 04 '14 at 21:17
  • @ArmenSafieh-Garabedian The problem is you can't pause the universe while you catch up. If you get behind, you either queue up previous events and try to catch up. Or you drop the events that you couldn't keep up with. (On a network for example, dropping packets during flooding is a big win over trying to reprocess all of them later causing the flood effects to last longer) In this case, very likely it's a bad idea to "hold open" the timer event threads you couldn't handle, instead you'd store the event notice on a queue which one thread would read during catch-up. –  Mar 04 '14 at 21:19
  • I actually just edited my answer again to basically say the same. You can throw away ticks while you're busy or you can queue and risk overflowing. – Erik Noren Mar 04 '14 at 21:22
  • @ErikNoren Thanks for the update Erik. I have added more context to the problem and will read your update in the mean time. – rex Mar 04 '14 at 21:24
  • @ArmenSafieh-Garabedian Note: I think your original question may be answered, I think it might be a good time to open a new question for the timer/threading issues. Please be sure and tell us what kind of timer you are using and just how fast it's firing. (It's getting too much for one question!!) I'd be surprised if what you have now was only one question more ;-) –  Mar 04 '14 at 21:27
  • Every update with additional detail is peeling an onion with more complexity that changes our answers. :) – Erik Noren Mar 04 '14 at 21:27
  • haha you are right guys, let me try to write this up in a new question before we get locked down :P – rex Mar 04 '14 at 21:30
  • @ebyrob Thanks for your help guys, please find the new question here: http://stackoverflow.com/questions/22183762/how-to-make-a-timer-wait-for-the-code-in-its-handler-to-finish-executing-as-a-so – rex Mar 04 '14 at 21:48
1

This isn't really an answer but:

UpdateBalance_PositionOpenned() is not being called in a loop, but is being called as part of a more complex event driven procedure that runs on the ticks of a timer (order of milliseconds)

see:

Multi-threading? – abatishchev 30 mins ago

Tight timer driven event-loop on the order of milliseconds probably has all the problems of threads, and will be almost entirely impossible to trouble-shoot with a step-through debugger. Stuff is happening way faster than you can hit 'F10'. Not to mention, you're accessing a variable from a different thread each event cycle, but there's no synchronization in sight.

  • Bingo!!! timer on order of milliseconds from threadpool means quite likely more than one thread is running through the code,and all the math there is unprotected from synchronization issues. That's it!!! – LB2 Mar 04 '14 at 20:48
  • @LB2 Blame abatishchev he got it first! Debug tracing (rather than step-through debugging) will often help here, especially if you use synchronization to slow things down as needed... –  Mar 04 '14 at 20:49
  • @ArmenSafieh-Garabedian +1 to you for learning something important! –  Mar 04 '14 at 20:52
0

Not really a full answer but too much for a comment
This is how I could code defensively
Local scope leads to less unexpected stuff
And it make code easier to debug and test

public void updateBalance(double amount, double fee, out double balance)
{
    try
    {
        balance = amount * (1.0 + fee);
        if (balance < 0.0) balance = 0.0;
    }
    catch (Exception Ex)
    {
        System.Diagnostics.Debug.WriteLine(Ex.Message);
        throw Ex;
    }
}

Value type is copied so even if then input variable for amount changed while the method was executing the value for amount in the method would not.

Now the out balance without locks is a different story.

paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • So if the tick event happened before this method finished executing I'd still have a problem right? – rex Mar 04 '14 at 21:00
  • It depends on how you handle balance outside the method. But at least the method has protected itself. If you are in a multithreaded environment it is problematic to refer to variables outside the method. There is an overhead as the value is copied in the method call. – paparazzo Mar 04 '14 at 21:25