Search code examples
c#multithreadingsystem.timers.timersystem.threading.channels

Retain variable values while running 2 or more tasks in parallel


I am working with timers and have implemented threading as first step in solving my problem. I have to send some reference every 30 seconds and this reference is created everytime a button is clicked. So I have the following code:

public MyReference SomeReference {get;set;}
public string MyFullName {get;set;} //this is bound to WPF textbox

public bool Saved()
{
     SomeReference.Id = GeneratedId;
     SomeReference.FullName = MyFullName;

     return SavedtoDB(SomeReference);
}

public void OnButtonClick()
{
    if(Saved()) //timer should only start if it is already saved in the database
         var t = new Thread(() => ExecuteTimer(SomeReference));
}

public void ExecuteTimer(MyReference someReference)
{
    System.Timers.Timer sendTimer = new System.Timers.Timer(30000);
    sendTimer.Elapsed += (sender, e) => ExecuteElapsed(sender, e, someReference, sendTimer);
    sendTimer.Start();
}

public void ExecuteElapsed(object source, ElapsedEventArgs e, MyReference someReference, System.Timers.Timer sendtimer)
{
    sendtimer.Stop();
    Send(someReference);
}

The following is the MyReference class:

public class MyReference()
{
    public string Id {get;set;}
    public string FullName {get;set;}
}

Edit for problem emphasis:

The problem is that it only sends the latest values someReference and disregards the previous ones. How will I send each values after 30 seconds without replacing the them with the latest value of someReference?


Solution

  • It seems like you need a deep clone of your object.

    Let me explain why a reference to your object is not enough. The SomeReference variable will always just point at a memory location where your MyReference instance is located. If you click your button this data is written/overwritten. When your timers fires it will always load whatever data is located at SomeReference and send it.

    Implementing a deep copy will physically copy your instance in memory so you will keep a copy of the object which can be used to send without being overwritten from your Save() call.

    To implement this you can (there are a lot of other methods) use the ICloneable interface like this:

      class MyReference : ICloneable
      {
        public string Id { get; set; }
        public string FullName { get; set; }
    
        public object Clone()
        {
          return new MyReference()
          {
            Id = this.Id,
            FullName = this.FullName
          };
        }
      }
    

    Now when clicking the button you can use Theodors Task.Delay method and give it a copy of your object:

        public void OnButtonClick()
        {
          //Here we now copy the complete object not just the reference to it
          var localCopyOfTheReference = (MyReference)someReference.Clone();
    
          var fireAndForget = Task.Run(async () =>
          {
            await Task.Delay(30000);
            Send(localCopyOfTheReference);
          });
        }