Сохранять значения переменных при одновременном выполнении 2 или более задач - PullRequest
0 голосов
/ 06 ноября 2019

Я работаю с таймерами и реализовал многопоточность как первый шаг в решении моей проблемы. Я должен отправлять ссылки каждые 30 секунд, и эта ссылка создается каждый раз, когда нажимается кнопка. Итак, у меня есть следующий код:

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

Ниже приведен класс MyReference:

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

Изменить для выделения проблем:

проблема в том, что он отправляет только самые последние значения someReference и игнорирует предыдущие. Как я буду отправлять каждое значение через 30 секунд, не заменяя их последним значением someReference?

Ответы [ 3 ]

2 голосов
/ 06 ноября 2019

Кажется, вам нужен глубокий клон вашего объекта.

Позвольте мне объяснить, почему ссылки на ваш объект недостаточно. Переменная SomeReference всегда будет указывать на область памяти, где находится ваш экземпляр MyReference. Если вы нажмете кнопку, эти данные будут записаны / перезаписаны. Когда ваши таймеры срабатывают, он всегда загружает все данные, расположенные в SomeReference, и отправляет их.

Реализация глубокой копии физически скопирует ваш экземпляр в память, так что вы сохраните копию объекта, который можно использовать. отправлять без перезаписи с вашего Save() вызова.

Для реализации этого вы можете (есть много других методов) использовать интерфейс ICloneable, например:

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

Теперь при нажатии на кнопку вы можете использовать метод Теодора Task.Delay и дать ему копию вашего объекта:

    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);
      });
    }
1 голос
/ 06 ноября 2019

Вот как вы можете запустить задачу, которая будет отправлять ссылку с 30-секундной задержкой, каждый раз, когда нажимается кнопка.

public void OnButtonClick()
{
    var localCopyOfTheReference = someReference;
    var fireAndForget = Task.Run(async () =>
    {
        await Task.Delay(30000);
        Send(localCopyOfTheReference);
    });
}

В случае исключения исключение будет проглочено.

0 голосов
/ 06 ноября 2019

Поддержание всех этих таймеров кажется действительно запутанным.

Почему бы просто не вести список случаев, когда вы хотите, чтобы событие произошло? И сделайте событие делегатом.

ConcurrentDictionary<DateTime,Action> events = new ConcurrentDictionary<DateTime,Action>();

Когда кто-то что-то щелкает,

events.TryAdd(DateTime.Now.AddSeconds(30), () => HandleEvent(whatever, you, want));

В обработчике убедитесь, что нет более позднего события.

void HandleEvent()
{
    if (events.Keys.Any( dateKey => dateKey > DateTime.Now )) return;

    DoStuff();
}

Теперь вам просто нужен способ, чтобы события происходили.

timer.Elapsed += (s,e) => {
    var list = events.Where( item => item.Key <= DateTime.Now ).ToList();
    foreach (var item in list)
    {
        item.Value.Invoke();
        events.TryRemove(item);
    }
};
...