0

我正在使用计时器,并已将线程作为解决问题的第一步。我必须每 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 的最新值替换它们?

4

3 回答 3

2

似乎您需要对对象进行深层克隆

让我解释一下为什么对您的对象的引用是不够的。该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
      };
    }
  }

现在,当单击按钮时,您可以使用 TheodorsTask.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);
      });
    }
于 2019-11-06T10:13:19.543 回答
1

以下是您如何启动一个任务,该任务将在每次单击按钮时延迟 30 秒发送参考。

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

如果出现异常,异常会被吞掉。

于 2019-11-06T08:55:44.917 回答
0

维护所有这些计时器似乎真的很令人困惑。

为什么不只是维护一个您希望事件发生的时间列表呢?并使事件成为代表。

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);
    }
};
于 2019-11-06T09:46:40.380 回答