Referenced data is reset to its default values after being copied - c#

I have an issue with data seemingly being reset to its default values.
The class is as follows (objectIDs is a simple enumeration):
public class Output_args: EventArgs {
public objectIDs outputtype;
public int internalID;
public int verdict;
public int outputID;
public long entrytime;
public Output_args Copy() {
Output_args args = new Output_args();
args.entrytime = this.entrytime;
args.internalID = this.internalID;
args.outputID = this.outputID;
args.outputtype = this.outputtype;
args.verdict = this.verdict;
return args;
}
}
The following code creates the object. It runs in a specific thread, let's say Thread1.
class Class1 {
EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
public event EventHandler<Output_args> newOutput;
public void readInput(){
List<Output_args> newoutputlist = new List<Output_args>();
/*
* code to determine the outputs
*/
Output_args args = new Output_args();
args.outputtype = objectIDs.stepID;
args.internalID = step[s].ID;
args.verdict = verdict;
args.entrytime = System.DateTime.Now.Ticks;
newoutputlist.Add(args.Copy());
if (newOutput != null && newoutputlist.Count > 0) {
// several outputs are being sent sequentially but for simplicity i've removed the for-loop and decision tree
try {
newOutput(null, newoutputlist[0].Copy());
} catch (Exception) { }
}
}
}
1 of the subscribers to this event has the following code. The processor method runs on a thread of a camerafeed. The newOutput event handler is being run on Thread1.
class Class2: Form {
private Output_args lastoutput = new Output_args();
public void newOutput(object sender, Output_args args) {
lock (lastoutput) {
lastoutput = args.Copy();
}
}
public void processor(){
lock (lastoutput) {
if (lastoutput.entrytime + 10000000 > System.DateTime.Now.Ticks) {
// do something
}
}
}
}
When the eventhandler 'newOutput' of Class2 is being called, the debugger shows that the copy works as expected and 'entrytime' is given the expected number of ticks.
However, when the processor method wants to read the 'entrytime', its value is 0. All other fields also have their default value assigned.
I've tried replacing the object 'lastoutput' with a simple field of the type long and removed the locks but the results are the same: it gets assigned properly in 'newOutput' but has its default value (0) in the processor method.
Any ideas on why this is happening?

you should not lock on the object lastoutput, but on another object, because you reassign the field.
The processor start and lock on the default field instance new Output_args() initialized with default values
class Class2: Form {
private object mylock = new object();
private Output_args lastoutput;
public void newOutput(object sender, Output_args args) {
lock (mylock) {
lastoutput = args.Copy();
}
}
public void processor(){
lock (mylock) {
if (lastoutput == null) {
//nothing to consume yet
}
else if (lastoutput.entrytime + 10000000 > System.DateTime.Now.Ticks) {
// do something
}
}
}
}
but this discard lastouput if consumer is slower than producer. You can use a queue ( or another collection ) as buffer if needed.
class Class2 {
private Queue<Output_args> outputs = new Queue<Output_args>();
public void newOutput(object sender, Output_args args) {
lock (outputs) {
outputs.Enqueue(args.Copy());
}
}
public void processor(){
lock (outputs) {
if (outputs.Count > 0) {
var lastoutput = outputs.Dequeue();
if (lastoutput.entrytime + 10000000 > System.DateTime.Now.Ticks) {
// do something
}
}
}
}
}
demo: https://dotnetfiddle.net/daHVD1

Related

c# Null Value Exception inside lock live data collection/aggregation

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.
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;
}
}
}

Object received as null when dispatched through an event

I have a fairly basic Logger
public class Logger: ILogger
{
public event EventHandler<MessageLoggedArgs> MessageLogged;
...
public void LogMessage(string msg, LogType logType)
{
var logItem = new LogItem(msg, logType);
addLogMessage(logItem);
}
private void addLogMessage(LogItem logItem)
{
MessageLogged?.Invoke(this, new MessageLoggedArgs(logItem));
}
}
public class MessageLoggedArgs
{
public LogItem LogItem {get;}
public MessageLoggedArgs(LogItem logitem)
{
LogItem = logItem;
}
}
public class LogsProcessor
{
public LogsProcessor(ILogger logger)
{
_logger = logger;
_logger.MessageLogged += OnMessageLogged;
}
private volatile List<LogItem> buffer = new List<LogItem>();
private ManualResetEvent ev = new ManualResetEvent(true);
private void OnMessageLogged(object sender, MessageLoggerArgs e)
{
buffer.Add(e.LogItem);
ev.Set();
}
//Runs on a thread
private void process()
{
while(true)
{
ev.WaitOne(Timeout.Infinite);
if(buffer.Count != 0)
{
while(buffer.Count != 0)
{
var item = logbuffer[0];
buffer.RemoveAt(0);
Application.Current.Dispatcher.BeginInvoke
( new Action( () => { //Log to UI }));
}
}
else
ev.Reset();
}
}
}
The consumer of this class receives some null objects when a lot of logs are generated. Is it possible that the objects are being garbage collected? If yes, then how is it possible that the logs are garbage collected, even though there is always a reference to the object alive inside of the MessageLoggedAgs.
This has me baffled.
Edit: Turns out that the problem is somehow because of accessing the buffer using a thread. I was able to eliminate the issue by using a ConcurrentQueue. However, I don't know where exactly the issue was earlier. I thought it wouldn't be an issue if I was accessing buffer using a single thread.

Multiple class instances raising same event

I'm having trouble figuring out what is wrong with my C# code.
I'm trying to learn how to use ConcurrentQueue class in System.Collections.Concurrent namespace.
In order to do this, I'm creating 2 instances of the same class in different threads, passing to the constructors a different Listbox control.
I am expecting each class instance of EventGenerator to raise events at random intervals, updating the Listbox the were passed with randomly generated number, and adding that number to a ConcurrentQueue which is also passed to the constructor.
In my main thread, is the method to DeQueue the ConcurrentQueue of objects EnQueued to it by both spawned threads.
But what I'm getting is the 2 EnQueue Listboxes displaying the same data and the DeQueue Listbox seeming reporting to have deQueued them both.
I apologize if my description is not good enough, and my code follows, along with a link to an image of my form in case it might better help visualize what I'm trying to do...
Form
public partial class Form1 : Form
{
ConcurrentQueue<int> CQ;
EventGenerator eventGenerator1;
EventGenerator eventGenerator2;
public Form1()
{
InitializeComponent();
CQ = new ConcurrentQueue<int>();
eventGenerator1 = new EventGenerator(CQ, listBox1);
eventGenerator1.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
eventGenerator2 = new EventGenerator(CQ, listBox2);
eventGenerator2.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
}
private void RandomEvent(object sender, IncomingConnectionEventArgs e)
{
string s = e.Property_Int.ToString()
+ " "
+ e.Property_String;
UpdateListbox(s, e.LB);
}
private void UpdateListbox(string argstring, ListBox argListBox)
{
if (InvokeRequired)
{
Invoke(new Action<string, ListBox>(UpdateListbox), new object[] { argstring, argListBox });
return;
}
int n;
bool b = false;
//do
//{
b = CQ.TryDequeue(out n);
//} while (!b);
argListBox.Items.Add(argstring);
argListBox.SelectedIndex = argListBox.Items.Count -1;
listBoxDeQueue.Items.Add(n.ToString());
listBoxDeQueue.SelectedIndex = listBoxDeQueue.Items.Count - 1;
}
private void button_Start_Click(object sender, EventArgs e)
{
Thread methodThread1 = new Thread(new ThreadStart(TheThread1));
methodThread1.Start();
Thread methodThread2 = new Thread(new ThreadStart(TheThread2));
methodThread2.Start();
}
private void TheThread2()
{
eventGenerator2.Start();
}
private void TheThread1()
{
eventGenerator1.Start();
}
private void button_Stop_Click(object sender, EventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
}
IncomingConnectionEventArgs
class IncomingConnectionEventArgs : EventArgs
{
public System.Windows.Forms.ListBox LB;
public int Property_Int { get; set; }
public string Property_String { get; set; }
public IncomingConnectionEventArgs(int argInt, string argString,
System.Windows.Forms.ListBox lb)
{
LB = lb;
Property_Int = argInt;
Property_String = argString;
}
}
EventGenerator
class EventGenerator
{
public delegate void RandomEventHandler(
object sender,
IncomingConnectionEventArgs e);
public event RandomEventHandler OnRandomEvent;
public Random r = new Random();
public ListBox listBox;
public bool Generate = true;
public ConcurrentQueue<int> CQ;
public EventGenerator(ConcurrentQueue<int> argCQ, ListBox argListBox)
{
CQ = argCQ;
listBox = argListBox;
}
public void Start()
{
Generate = true;
while (Generate)
{
Thread.Sleep(r.Next(100, 2000));
RandomEvent();
}
}
public void Stop()
{
Generate = false; ;
}
public void RandomEvent()
{
if (OnRandomEvent == null)
{
return;
}
int n = r.Next(1000, 10000);
CQ.Enqueue(n);
IncomingConnectionEventArgs Args =
new IncomingConnectionEventArgs(n, "", listBox);
OnRandomEvent(this, Args);
}
}
The problem is with your use of Random. Unless you use a single instance of Random or explicitly seed each instance differently, the two threads calling Random.Next(...) will typically generate the same values.
A better way to seed your instance is this:
Random r = new Random(Guid.NewGuid().GetHashCode());

Visual C# returning value from an event handler

I have a form that has a button to get a method executed in another class.
Code on the form:
public delegate void CustomPreviewCreate();
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
CustomPreviewCreate_Do();
}
}
This event then gets handled in another class. What I would like to achieve is that I can feed back to the form some form of return value if the method correctly executed.
What I tried so far does not get me the result.
Here is the code:
public void Initialize()
{
SubAsstViewPartControl.CustomPreviewCreate_Do += SubAsstViewPartControl_CustomPreviewCreate_Do;
// this gives me a the compiler error that the return type is wrong
}
private bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return false;
}
Is there any direct way to return value from an event handler or I need to use a separate static field to store the event result in?
Update:
Per #Jon's comment, which seemed the simplest to me, I added an answer below demonstrating the simplest approach.
The common approach is to encapsulate your value in the type of EventArgs your event expects. For example, the Framework's CancelEventArgs contains a settable bool Cancel property, allowing each CancelEventHandler to assign a value. The sender can then read the property after the event has been invoked. You could also use a container-like EventArgs class if you want to collect separate values from individual event handlers. For example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class SingleValueEventArgs : EventArgs
{
public int Value { get; set; }
}
public class MultiValueEventArgs : EventArgs
{
private List<int> _values = new List<int>(); // Private to prevent handlers from messing with each others' values
public IEnumerable<int> Values
{
get { return _values; }
}
public void AddValue(int value) { _values.Add(value); }
}
public class Exposer
{
public event EventHandler<SingleValueEventArgs> WantSingleValue;
public event EventHandler<MultiValueEventArgs> WantMultipleValues;
public void Run()
{
if (WantSingleValue != null)
{
var args = new SingleValueEventArgs();
WantSingleValue(this, args);
Console.WriteLine("Last handler produced " + args.Value.ToString());
}
if (WantMultipleValues != null)
{
var args = new MultiValueEventArgs();
WantMultipleValues(this, args);
foreach (var value in args.Values)
{
Console.WriteLine("A handler produced " + value.ToString());
}
}
}
}
public class Handler
{
private int _value;
public Handler(Exposer exposer, int value)
{
_value = value;
exposer.WantSingleValue += exposer_WantSingleValue;
exposer.WantMultipleValues += exposer_WantMultipleValues;
}
void exposer_WantSingleValue(object sender, SingleValueEventArgs e)
{
Console.WriteLine("Handler assigning " + _value.ToString());
e.Value = _value;
}
void exposer_WantMultipleValues(object sender, MultiValueEventArgs e)
{
Console.WriteLine("Handler adding " + _value.ToString());
e.AddValue(_value);
}
}
class Program
{
static void Main(string[] args)
{
var exposer = new Exposer();
for (var i = 0; i < 5; i++)
{
new Handler(exposer, i);
}
exposer.Run();
}
}
}
Per Jon Skeet's comment, which seemed the simplest to me, the simplest approach seems to be as follows:
public delegate bool CustomPreviewCreate(); // here we declare a return type
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
bool returnval = CustomPreviewCreate_Do();
}
}
And then:
// the method is declared to return the same type
bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return true; // return the value of the type declared
}

Event Handling with Timer to Poll Hardware

I need to request values using functions in a DLL provided by the manufacturer of my particular piece of hardware (a weather station). I'm new to C#, and the concepts of delegates/events are tough to wrap my head around. Nonetheless, I've managed to pull the functions from the DLL and verify that data makes it through. My issue is with polling the instrument periodically with a Timer. In Initialize(), an object is instantiated, but the event isn't handled leaving the object null. I'm out of ideas, and would like some advice!
public class HardwareData : EventArgs
{
public float OutsideTemp { get; set; }
public int OutsideHum { get; set; }
public float WindSpeed { get; set; }
public int WindDirection { get; set; }
}
public class Hardware : IDisposable
{
private static Hardware v;
private System.Timers.Timer hardwareTimer;
private int counter = 0;
private static readonly object padlock = new object();
public static Hardware Instance
{
get
{
lock (padlock)
{
if (v == null)
v = new Hardware();
return v;
}
}
}
public void Initialize()
{
try
{
hardwareTimer = new System.Timers.Timer(500);
hardwareTimer.Elapsed += new ElapsedEventHandler(hardwareTimer_Elapsed);
HardwareVue.OpenCommPort_V(3, 19200); //COM port and baud rate are verified.
hardwareTimer.Start();
}
catch (Exception ex)
{
throw new InvalidOperationException("Unable to initialize.", ex);
}
}
public HardwareData LastHardware { get; set; }
void hardwareTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
counter += 1;
Console.WriteLine(counter);
HardwareVue.LoadCurrentHardwareData_V();
HardwareData v = new HardwareData()
{
OutsideTemp = HardwareVue.GetOutsideTemp_V(),
OutsideHum = HardwareVue.GetOutsideHumidity_V(),
WindSpeed = HardwareVue.GetWindSpeed_V(),
WindDirection = HardwareVue.GetWindDir_V()
};
LastHardware = v;
}
catch (Exception) { }
}
public void Dispose()
{
HardwareVue.CloseCommPort_V();
hardwareTimer.Stop();
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
Hardware test = new Hardware();
try
{
if (test != null)
{
test.Initialize();
test.Dispose();
Assert.AreEqual(0, test.LastHardware.OutsideHum);
}
}
catch (NullReferenceException ex)
{
Console.WriteLine("Object is null.");
}
// Console.WriteLine(test.LastHardware.OutsideHum);
}
}
When working with timers, you need to enable the timer and make sure the events are rasied:
hardwareTimer.Enabled = true;
hardwareTimer.CanRaiseEvents = true;
For Reference: Timers on MSDN
Edit
In addition to the other comments to both the OP's question and this answer, the issue with the LastHardware being null is due to the property never being instantiated before the timer initially fires. To resolve this, you should instantiate the LastHardware property in the default constructor (or in the Initialize method):
public Hardware()
{
LastHardware = new HardwareData();
}
Of course, you'd want to set some default values upon instantiation.

Categories