multi-threaded request counter

P

Peter

Hi

In a multi-threaded application I need to keep track of request-counts
for various "users". That is, when a user makes a request to my
application I need to increment a counter to track the number of times
the user has made a request.

But a "user" is more than a single entity, and the same user can be
calling my application simultaneously.

How do I keep track of a user's requests? If I use a simple Hashtable
(with the users' unique ids as keys) to contain the counts, when I get
the count from the Hashtable to update it, the same user could be
making another request simultaneously, and the counts will not be
accurate.

I don't want to lock the entire Hashtable, because I am worried that
will badly affect performance - as if several different users are
calling simultaneously then they will have to wait for each other.

Any ideas to how I can implement a counter for user requests?


Thanks,
Peter
 
J

Jon Skeet [C# MVP]

Peter said:
In a multi-threaded application I need to keep track of request-counts
for various "users". That is, when a user makes a request to my
application I need to increment a counter to track the number of times
the user has made a request.

But a "user" is more than a single entity, and the same user can be
calling my application simultaneously.

How do I keep track of a user's requests? If I use a simple Hashtable
(with the users' unique ids as keys) to contain the counts, when I get
the count from the Hashtable to update it, the same user could be
making another request simultaneously, and the counts will not be
accurate.

I don't want to lock the entire Hashtable, because I am worried that
will badly affect performance - as if several different users are
calling simultaneously then they will have to wait for each other.

Any ideas to how I can implement a counter for user requests?

Rather than *worry* about locking the entire hashtable badly affecting
performance, *measure* it. You only need to lock for the amount of time
it takes to fetch the entry and update it.

Unless you are processing hundreds of thousands of requests per second,
I doubt it will actually affect performance that badly.
 
M

Marc Gravell

I don't want to lock the entire Hashtable, because I am worried that
will badly affect performance - as if several different users are
calling simultaneously then they will have to wait for each other.

They'd only need to be synchronized while logging that a request happened -
not for the actual duration of the request. In most cases I would imagine
that this won't create a bottleneck (compared to everything else going on),
so until profiling *shows* that you have a problem, I'd use synchronized
access to a dictionary<something, int> where something relates to your user
id.

Something like (off the cuff code):

static class UserCounters
{
private static readonly Dictionary<string, int> counter =
new
Dictionary<string,int>(StringComparer.InvariantCultureIgnoreCase);
public static void LogAccess(string cn)
{
lock (counter)
{
int value;
if (counter.TryGetValue(cn, out value))
{
counter[cn] = value + 1;
}
else
{
counter.Add(cn, 1);
}
}
}
public static KeyValuePair<string, int>[] Dump(bool reset)
{
lock (counter)
{
KeyValuePair<string, int>[] data = new KeyValuePair<string,
int>[counter.Count];
int index = 0;
foreach (KeyValuePair<string, int> pair in counter)
{
data[index++] = pair;
}
if (reset)
{
counter.Clear();
}
return data;
}
}
}
 
P

Peter

Marc said:
They'd only need to be synchronized while logging that a request
happened - not for the actual duration of the request.
public static KeyValuePair<string, int>[] Dump(bool reset)
{
lock (counter)
{
KeyValuePair<string, int>[] data = new KeyValuePair<string,
int>[counter.Count]; int index = 0;
foreach (KeyValuePair<string, int> pair in counter)
{
data[index++] = pair;
}
if (reset)
{
counter.Clear();
}
return data;
}
}

OK, thanks for the answer (and to Jon), and for the code - which is
similar to what I had.

It touches on another problem though, because in my system I also need
to store data besides the simple int count. So I made a simple data
object to hold all the user-request-count data I needed, but soon
discovered that with a method like your Dump, the client receives a
data object which it could then alter - upsetting the values stored in
the hashtable.

Is the way to avoid this to return data-objects which only have getters
(no setters) - and/or to actually return copies of the real
data-objects from the hashtable?


/Peter
 
L

Lasse Vågsæther Karlsen

Peter said:
Marc said:
They'd only need to be synchronized while logging that a request
happened - not for the actual duration of the request.
public static KeyValuePair<string, int>[] Dump(bool reset)
{
lock (counter)
{
KeyValuePair<string, int>[] data = new KeyValuePair<string,
int>[counter.Count]; int index = 0;
foreach (KeyValuePair<string, int> pair in counter)
{
data[index++] = pair;
}
if (reset)
{
counter.Clear();
}
return data;
}
}

OK, thanks for the answer (and to Jon), and for the code - which is
similar to what I had.

It touches on another problem though, because in my system I also need
to store data besides the simple int count. So I made a simple data
object to hold all the user-request-count data I needed, but soon
discovered that with a method like your Dump, the client receives a
data object which it could then alter - upsetting the values stored in
the hashtable.

Is the way to avoid this to return data-objects which only have getters
(no setters) - and/or to actually return copies of the real
data-objects from the hashtable?


/Peter

What exactly is in this "counter" of yours?

If you want precise and helpful answers, you need to give precise and
helpful information as well. A simple counter can be done like the other
posts here have indicated, but something more advanced might need
something more advanced.

Do you need realtime up to date statistics about something? Or do you
just need to know at some later date? If so, perhaps a simple log could
do that wouldn't necessarily need to interfere with previous data at
all, and then some sort of program that tallied up the results when
needed...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top