0

Lets suppose I have a single class that has two methods OnBarEvent & Update.

Update: subscribes to a continuous stream of data that comes in asynchronously.

OnBarEvent: will send an event out every n minutes. I am using a timer class to keep track of time and then just have SendEvent attached to the timer class event handler. Essentially, this method will be called whenever N minutes pass

The program will receive asynchronous data via OnEvent which will just summarize the data over a period of time. Once a specified time has passed, then the SendEvent will be called

namespace Common.Aggregator
{

    public class BaseTimeAggregator
    {
        //The last time we emitted a consolidated bar
        private DateTime? _lastEmit;
        //The minimum timespan between creating new bars.
        private readonly TimeSpan? _period;
        //The working bar used for aggregating the data
        private Bar _workingBar;
        //The last working bar
        private Bar l_workingBar;
        //The Start Time 
        private DateTime StartTime;
        private System.Timers.Timer timer;
        public new event EventHandler<Bar> DataConsolidated;



        private void OnBarEvent(Object source, System.Timers.ElapsedEventArgs e)
        {


            if (DateTime.Now > StartTime)
            {
                if (_workingBar != null)
                {
                    //Console.WriteLine("New Bar: {0}", e.SignalTime);


                    lock (_workingBar)
                    {
                        // Fire Bar
                        var workingTradeBar = _workingBar as Bar;

                        if(l_workingBar == null)
                        {

                            decimal close_ret = workingTradeBar.Close / workingTradeBar.PreClosePrice;
                            workingTradeBar.Logret = (decimal)Math.Log((double)close_ret);

                        }
                        else
                        {

                            // PROBLEM: workingTradeBar can be null here for some reason
                            decimal value = workingTradeBar.Close / l_workingBar.Close;
                            workingTradeBar.Logret = (decimal) Math.Log((double)value);

                        }



                        l_workingBar = workingTradeBar;
                        DataConsolidated(this, workingTradeBar);
                        _workingBar = null;
                    }

                }

            }
        }


        public void Update(Tick data)
        {
            AggregateBar(data);
        }



        protected void AggregateBar(Tick data)
        {

            // Create New Bar
            if (_workingBar == null)
            {
                _workingBar = new Bar(data.LastPrice, data.LastPrice, data.LastPrice, data.LastPrice);
                _workingBar.PreClosePrice = data.PreClosePrice;

            }


            lock (_workingBar)
            {
                // In the case it got accessed in between 
                if (_workingBar == null)
                {
                    _workingBar = new Bar(data.LastPrice, data.LastPrice, data.LastPrice, data.LastPrice);
                    _workingBar.PreClosePrice = data.PreClosePrice;

                }


                // Update Bar
                _workingBar.Update(data.DataType, data.LastPrice, data.BidPrice, data.AskPrice,
                        data.Volume, data.BidSize, data.AskSize);

            }

        }




            return new DateTime(
                dateTime.Year,
                dateTime.Month,
                dateTime.Day,
                hours,
                minutes,
                seconds,
                milliseconds,
                dateTime.Kind);
        }
    }
}

The problem I am running in to is that within the lock, when I access the workingTradeBar variable (see commented code above where "PROBLEM"), there are situations where its null and throws a system.null error. I can't figure out how it can be null given I made a check right before I entered the lock. Also, the only place I set it null is in the same method since I want to start summarizing the data after N minutes passed.

Thanks

This is different from the other question because its purely a multi-threading problem/race condition.

user1234440
  • 18,511
  • 17
  • 51
  • 88
  • "_Null value exception_", "_system.null error._" Never heard about such things in relation to C#/.NET. Are you trying to say "NullReferenceException"? –  May 07 '19 at 18:39
  • 1
    Possible duplicate of [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) –  May 07 '19 at 18:41
  • 1
    Note that your problem might possibly be caused by a race condition. I don't know at which times and under which circumstances _OnBarEvent_ will be called, but somewhere in your code in another thread, `_workingBar` seems to be set to null after the check `if (_workingBar != null)` but before the assignment `var workingTradeBar = _workingBar as Bar;` is being executed. This could (aside from other possibilities) perhaps be due to two or more invocations of _OnBarEvent_ running concurrently, for example. (according to your explanation and your code snippet in the question) –  May 07 '19 at 18:45
  • The problem in shared variable *_workingBar*. Could you get rid of from it and get the current bar in *OnBarEvent* as *var workingBar = (Bar)source*? – vladimir May 07 '19 at 19:11
  • Simple.... Many threads check if it is null.... they all get that it is not null... the thread inside the lock sets it to null then the next thread enters the lock and it is now null.. Your check for null and the set to null should be atomic, they should both be inside the lock. – Jonathan Alfaro May 07 '19 at 19:14
  • So `OnBarEvent` is called every 15 minutes on the dot by internal timer class. But the thing is the `Update` gets called any time from a subscribed server from the market. – user1234440 May 07 '19 at 19:27
  • so I am trying to build a 15 minute bar from tick data coming in and I need to flush out the workingBar every 15 minute and start fresh again. The ONLY place I flush it out is inside `OnBarEvent` which makes this even more confusing, – user1234440 May 07 '19 at 19:28
  • also at the 15 minute dot, I will need to have the OnBarEvent have priority to send out the new bar. – user1234440 May 07 '19 at 19:29
  • Why do you have the `as Bar` in the line `var workingTradeBar = _workingBar as Bar;`? The variable `_workingBar` is typed to `Bar` in its declaration so you never need to use `as` to cast it, it will always be an instance of `Bar` or null. – mortb May 07 '19 at 19:46
  • @mortb you are right, that line is not necessary – user1234440 May 07 '19 at 19:52
  • @user1234440 have you solved your problem? – mortb May 09 '19 at 10:04
  • no unfortunately not, i still get the error. the main problem is I am not sure what is making it null other than the `OnBarEvent`. – user1234440 May 09 '19 at 22:11

1 Answers1

0

Some remarks:

  • Event handlers should get the current instance from input params, not from the local variable.
  • If required need store references to all Bars.
  • Any method that manipulates the Bars should be synchronized.
  • As alternative the lock-statement can be used ReaderWriterLockSlim.
namespace Common.Aggregator
{

    public class BaseTimeAggregator
    {

        // REMOVE this field    
        // --> private Bar _workingBar;


        private readonly object _lock = new object();
        private readonly Dictionary<int, Bar> _barDictionary = new Dictionary<int, Bar>();



        private void OnBarEvent(Object source, System.Timers.ElapsedEventArgs e)
        {
            var bar = (Bar)source;

            // Manipulate with the actual instance defined in 'bar'-variable ..
        }

        public void Update(Tick data)
        {
            lock (_lock) {          
              AggregateBar(data);
            }
        }

        public void Smth_method(int barId)
        {
            lock (_lock) {          
                var bar = _barDictionary[uniqueBarId];
                // ..
            }
        }

        protected void AggregateBar(Tick data)
        {
          var uniqueBarId = data.{some param that identify bar};

          if (_barDictionary.ContainsKey(uniqueBarId)) {
            _barDictionary[uniqueBarId].Update(data.DataType, data.LastPrice, data.BidPrice, data.AskPrice, data.Volume, data.BidSize, data.AskSize);
            return;
          }

          var bar = new Bar(data.LastPrice, data.LastPrice, data.LastPrice, data.LastPrice);
          bar.PreClosePrice = data.PreClosePrice;

          _barDictionary[uniqueBarId] = bar;           
        }

    }
}
vladimir
  • 8,809
  • 2
  • 23
  • 47