2

尽管最终用户发出请求,但仅在特定时间间隔后调用函数

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

这是我的功能,当有点击动作时会触发。

我希望仅在特定时间间隔(例如 5 分钟)调用它。尽管用户在时间间隔之前发出请求......

我正在尝试使用计时器来达到我的目的,但问题是每次用户单击时,计时器都会再次启动..那么计时器如何控制功能

PS:我无法在点击的用户应用程序中进行更改

4

4 回答 4

2

首先你应该在你的类中添加一个字典:

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

如果你或多或少地做一些类似的事情,你就会有你需要的行为。

于 2013-05-08T10:07:24.443 回答
1

作为一个快速的解决方案,我可以建议保留一个委托列表以及您要在每个计时器滴答时调用的方法。因此,在用户操作中,您只需添加要调用的方法。如果每个用户调用的参数必须是唯一的,您可以执行以下代码中的操作:

// 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 应用程序,那么每个用户都在他/她自己的线程中运行,因此多个用户同时也会增加并发性。此外,您可能必须添加适当的错误处理,因为为简洁起见,我省略了该部分。


更新

正如评论中所建议的,您可能会受益于使用与列表不同的集合来保存操作。这取决于您的场景的实际需求。

于 2013-05-08T09:50:57.630 回答
0

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

当计时器滴答时调用TryDequeue和处理消息。

于 2013-05-08T10:43:23.020 回答
0

我在当前系统中做了类似的事情。就我而言,我正在批量处理数据请求,这样我就可以将它们组合成一个,而不是多次调用远程服务。

这是系统的高级概述:

我有一个 Collat​​orClass,它有一个方法 MakeRequest() 在内部, Collat​​orClass 有一个 Timer 对象,该对象初始化为 null 和一个包含您的请求详细信息的任何对象的列表。

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

当计时器计时结束时,系统会从列表中读取所有请求的详细信息并采取适当的措施。(请注意,您可能需要使用 lock 以使所有这些线程安全)。

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

如果您想了解更多详情,请告诉我。

于 2013-05-08T09:54:01.413 回答