Raghu
Raghu

Reputation: 323

Control the function call via Timer c#

Calling a function only after certain time interval although end user makes a request

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

this is my function which will be triggered when there is a click action.

I want this to be called only at certain time intervals say 5 mins.Although the user makes a Request before the time interval...

I am trying to have a timer to serve my purpose but the problem is every time the user clicks then timer would start again..so that how could a function be controlled by timer

PS:I can not make changes in user application where the click is made

Upvotes: 2

Views: 1955

Answers (4)

stackh34p
stackh34p

Reputation: 8999

As a quick solution I can suggest to keep a list of delegates with the method you want to invoke on each timer tick. So on user action you just add the method you want to invoke. If the parameters must be unique for each user call, you might do something like in the following code:

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

In the code above, you must make sure you are using the same ActionExecutor instance where you handle the user actions. Notice the synchronization with the locks - timers usually run in separate thread than the thread adding the action. If you are speaking of a web application, then each user is running int his/her own thread, so multiple users at once will also add concurrency. Also, you may have to add appropriate error handling, as I have omitted that part for brevity.


Update

As suggested in comments, you might benefit from using a different collection than a list to hold the actions. It depends on the actual needs of your scenario.

Upvotes: 1

ppetrov
ppetrov

Reputation: 3105

First you should add a Dictionnary to your class:

Dictionnary<int, NotificationMessageRequest> _notifications

Then when a user clicks on the button, you should execute this function: (or change the one that's executed now so that it looks like this)

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

Then call this method on your timer event:

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

        _notifications.Clear();
    }
}

If you do something more or less like that you'll have the behavior you need.

Upvotes: 2

Kaveh Shahbazian
Kaveh Shahbazian

Reputation: 13513

Create a timer and set it's interval to a proper value. Then declare a field of type System.Collections.Concurrent.ConcurrentQueue<YourMessageType> (static if it fits) and put your message in the queue in the OnClick.

When timer ticks call TryDequeue and process messages.

Upvotes: 0

Steve
Steve

Reputation: 1316

I do something similar in my current system. In my case, I'm batching data requests so that rather than making multiple calls to a remote service, I can combine them into one.

This is a high level overview of the system:

I have a CollatorClass that has one method MakeRequest() Internally, CollatorClass has a Timer object that is initalised to null and a List of whatever objects hold your request details.

When MakeRequest is called: The timer is started if it's null. The details of the request are added to a list.

When the timer ticks over, the system reads the details of all requests from the list and takes the appropriate actions. (Note that you may need to make use of lock in order to make all this thread safe).

If you want, you can also pass an Action reference to be called when the data request is actually performed. Not sure if this is necessary in your case though.

Let me know if you'd like more detail.

Upvotes: 0

Related Questions