-4

I have a method that use an API of another system. My system to run on a list of data and for each item in the list it create a thread with request to the other system. There is only one problem, the other system can handle x amount of request every y time. For example, Handling 5 requests every 2 minutes. What is the best practice to implement it? lets say that i want to run over the list only one time.

To make it more clear, lets say i approach some service via the internet and this service allow me to send 5 requests every 2 minutes, i have an xml with 1000 rows and each row is a request i want to read this xml and send requests but i don't want the other system to deny me i want to send 5 request every 2 minutes. Thanks.

roy barak
  • 53
  • 5
  • 2
    Please update your post to show your existing code. – mjwills Sep 14 '17 at 11:12
  • 1
    Possible duplicate of [c# Threadpool - limit number of threads](https://stackoverflow.com/questions/10342057/c-sharp-threadpool-limit-number-of-threads) – Sinatr Sep 14 '17 at 11:13
  • @Sinatr, I'm not sure what he's asking, but it appears to be a completely different question – Yair Halberstadt Sep 14 '17 at 11:14
  • @roy not exactly the same but an inspiration: https://stackoverflow.com/q/16360733/1207195 – Adriano Repetti Sep 14 '17 at 11:15
  • What i was asking is that i have 2 different limitation to run the thread, one can be solved with semaphore , the amount of threads that can run, the other is interval limation i can run x amount of thread every y amount of time. – roy barak Sep 14 '17 at 11:25
  • 1
    Why would you use multiple threads if you can't go faster than 5 requests per 2 minutes anyway? – C.Evenhuis Sep 14 '17 at 11:29
  • @C.Evenhuis only an example this numbers are not real. – roy barak Sep 14 '17 at 11:32
  • 1
    Why would you use multiple threads if you can't go faster than *a certain amount of requests per minute* anyway? – C.Evenhuis Sep 14 '17 at 11:35
  • @C.Evenhuis Why not? i read it one time from a file and not handling the file again now the system free to do other things while this happening in the background in separate threads. – roy barak Sep 14 '17 at 11:39
  • @roybarak You would still have that advantage when using just a single background thread, but it would make your problem simpler to solve - you'd only have to ensure a certain amount of time between the start of two consecutive requests. – C.Evenhuis Sep 14 '17 at 11:50
  • There's no reason to create multiple threads *at all* here. This is all IO bound work. You don't need additional threads to do IO bound work. Threads are there to help you do CPU bound work. – Servy Sep 14 '17 at 13:38
  • Lets say that one request is readFile and other is writeFile. – roy barak Sep 14 '17 at 14:58

2 Answers2

0

You can do this a number of ways. Perhaps the easiest is a single thread that does a Thread.Sleep() between calls.

Five requests every 2 minutes works out to one request every 24 seconds. So you could write:

while (requests remaining)
{
    make next request
    Thread.Sleep(TimeSpan.FromSeconds(24));
}

That actually gives you one request every 24 seconds plus the request time. If you wanted it to be exactly 24 seconds, you'd have to time how long it takes to make the request, and subtract that from the delay.

Here's how you'd do that:

while (requests remaining)
{
    var sw = Stopwatch.StartNew();
    make next request
    sw.Stop();
    var sleepTime = 24000 - sw.ElapsedMilliseconds;
    Thread.Sleep(sleepTime);
}        

Another way to do it is with a timer. That saves you having to wait on a thread all the time. Create a System.Threading.Timer with a 24-second period, and have the callback method do the request.

Both of those techniques will prevent you from making requests too quickly, and they're very easy to code.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • If there were 5 requests to execute, and each request took 3 seconds to execute, how long would this implementation take to execute the 5 requests? – mjwills Sep 14 '17 at 13:42
  • @mjwills: It would take about 27 seconds per request. As I said, the actual time is 24 seconds *plus the request time*. I also pointed out that you can fix that easily enough by subtracting the last request time from the sleep period. Perhaps I'll add that. Oh, and the timer-based alternative would not suffer from the issue you pointed out. – Jim Mischel Sep 14 '17 at 13:44
  • So roughly 99 seconds (24 * 4 + 3)? Would it be possible to run all 5 of them at once and complete them in a total of 3 seconds instead? – mjwills Sep 14 '17 at 13:49
  • @mjwills: It's certainly possible to execute all the requests in parallel and then wait 2 minutes before doing it again. I found it easier to just space the requests out over time. The result is almost the same. The only difference is that one way would complete two minutes before the other. That two minutes isn't terribly significant when you're making thousands of requests at the rate of one every 24 seconds. – Jim Mischel Sep 14 '17 at 13:52
  • I will add here something to the question. Like i said i have list of request that i need to make but i have 3 different systems that i ask each system have different rules and by rules i mean the amount of requests it can handle each time, system 1 can handle 20 requests in every 2 minutes, system 2 can have 60 requests in 5 minutes just an example. I want for each item to send request and if i got to the max request i am allow i need to wait the shortest time and then send again te amount i am allowed. – roy barak Sep 14 '17 at 14:55
  • 2
    @roybarak: That's rather important information that affects the solution. Please edit your question and add that information. I'll modify my answer to address that. – Jim Mischel Sep 14 '17 at 16:35
-1

You may wish to consider using Parallel.ForEach to iterate over the list.

You should also set MaxDegreeOfParallelism to 5 to limit the number of threads.

You will also need to keep track of number of calls per 2 minutes (manually) as per your requirements. The easiest way to do this would be to store the start DateTime of each request in a ConcurrentLimitedCollection<DateTime>. If the ConcurrentLimitedCollection.CurrentSize == 5 then you will need to verify that the first entry is at least 2 minutes in the past.

ConcurrentLimitedCollection is available here.

mjwills
  • 23,389
  • 6
  • 40
  • 63
  • Thanks i will check this solution – roy barak Sep 14 '17 at 11:40
  • Rather a complicated solution to a pretty simple problem, don't you think? – Jim Mischel Sep 14 '17 at 13:38
  • @JimMischel Thanks for the feedback. Which part of it do you find complicated? What do you suggest as an alternative? – mjwills Sep 14 '17 at 13:39
  • I did add an alternative. And no, I'm not the person who downvoted your answer. My only suggestion here would be to re-read the OP's question and decide if it really requires threading at all. – Jim Mischel Sep 14 '17 at 13:42
  • I wouldn't say it **requires** threading. Does it **benefit** from threading? Well, I can only assume it does if the OP is already using it. :P And threading will almost definitely result in a quicker response time if the number of requests per time window is low (e.g. 4 every 2 minutes if the limit is 5 every 2 minutes). – mjwills Sep 14 '17 at 13:46
  • @mjwills Creating threads just to sit around waiting for a network request to complete or for time to pass isn't productive. There's no need to create threads if you don't actually have CPU bound work for them to do. – Servy Sep 14 '17 at 13:58