通过定时器c#控制函数调用



即使最终用户发出请求,也只在一定时间间隔后调用函数

public override void SendNotificationMessage(NotificationMessageRequest request)
    {......}

这是我的函数,当有点击动作时将被触发。

我希望它只在特定的时间间隔(比如5分钟)被调用。尽管用户在时间间隔之前发出请求…

我试图有一个定时器来服务于我的目的,但问题是每次用户点击然后计时器会重新开始..这样一个函数怎么能由定时器控制

PS:我不能在用户应用程序中更改点击

首先你应该给你的类添加一个dictionary:

Dictionnary<int, NotificationMessageRequest> _notifications

当用户点击按钮时,你应该执行这个函数:(或者改变现在执行的函数,使它看起来像这样)

private void PlanNotification(NotificationMessageRequest request, int userId)
{
    // or grab the userId from where you can
    lock(_notifications) // if needed ?
    {
        if (_notifications.Contains(userId)) return;
        _notifications.Add(userId, request);
    }
}

然后在你的计时器事件上调用这个方法:

private void SendNotifications()
{
    lock(_notifications)
    {
        foreach(KeyValuePair<int, NotificationMessageRequest> p in _notifications)
            SendNotificationMessage(p.Value);
        _notifications.Clear();
    }
}

如果你做了或多或少这样的事情,你就会有你需要的行为。

作为一个快速的解决方案,我可以建议在每个计时器滴答时保留一个包含您想要调用的方法的委托列表。在用户动作上,你只需要添加你想调用的功能。如果每个用户调用的参数必须是唯一的,则可以执行如下代码:

// This is the class handling the timer ticks
public class ActionExecutor : IDisposable
{
    private Timer timer;
    private IList<Action> actionsList = new List<Action>();
    public ActionExecutor()
    {
        // choose the interval as suits you best, or use 
        // constructor argument
        timer = new Timer(1000);
        timer.Elapsed += OnTimerTick;
        timer.AutoReset = true;
        timer.Start();
    }
    void OnTimerTick(object sender, ElapsedEventArgs)
    {
        lock(actionsList)
        {
            foreach(Action a in actionsList) 
            {
                a();
            }
            actionsList.Clear();
        }
    }
    public void AddAction(Action a)
    {
        lock(actionList)
        {
            actionList.Add(a);
        }
    }
    public void Dispose()
    {
        // we must stop the timer when we are over
        if (timer != null)
        {
            timer.Stop();
        }
        if (actionList != null)
        {
            actionList.Clear();
        }
    }
    ~ActionExecutor()
    {
        Dispose();
    }
}

//handling user click in your application
void OnUserClick() 
{
    // built the specific notification request
    NotificationMessageRequest request = ...
    actionExecutor.AddAction(() => SendNotificationMessage(request));
}

在上面的代码中,您必须确保使用处理用户操作的同一个ActionExecutor实例。注意与锁的同步——计时器通常在单独的线程中运行,而不是在添加动作的线程中运行。如果你说的是一个web应用程序,那么每个用户都在他/她自己的线程中运行,所以多个用户同时运行也会增加并发性。此外,您可能必须添加适当的错误处理,因为为了简洁起见,我省略了这一部分。


正如注释中建议的那样,使用不同的集合而不是列表来保存操作可能会受益。这取决于您的场景的实际需要。

我在我当前的系统中做了类似的事情。在我的例子中,我对数据请求进行批处理,这样就可以将它们合并为一个,而不是对远程服务进行多个调用。

这是系统的高级概述:

我有一个CollatorClass,它有一个方法MakeRequest()在内部,CollatorClass有一个初始化为null的Timer对象和一个包含请求详细信息的任何对象的List。

当调用MakeRequest时:如果它为空,则启动计时器。请求的详细信息被添加到一个列表中。

当计时器结束时,系统从列表中读取所有请求的详细信息并采取适当的操作。(注意,为了保证所有线程的安全,你可能需要使用lock)。

如果需要,还可以传递一个Action引用,以便在实际执行数据请求时调用。我不确定这在你的情况下是否必要。

创建一个计时器,并将其间隔设置为适当的值。然后声明一个类型为System.Collections.Concurrent.ConcurrentQueue<YourMessageType>的字段(如果适合的话是静态的),并将您的消息放入OnClick的队列中。

当定时器滴答时调用TryDequeue并处理消息

最新更新