I'm trying to implement a timeout mechanism for WCF calls that don't support CancellationTokens or pre-defined timeouts. In order to do that, I created a small project and came up with this structure:
var st = Stopwatch.StartNew();
try
{
var responseTask = Task.Run(() =>
{
var task = new WaitingService.ServiceClient().GetDataAsync(60000);
if (!task.Wait(1000))
{
return null;
}
return task.Result;
});
await responseTask;
}
catch { }
return Ok("Awaited for " + st.ElapsedMilliseconds + "ms. Supposed to be " + sleep);
When I run this code on my local machine sequentially (1 call at a time) the output is very VERY
close to 1000, missing by around 10 to 50ms, which is 100% acceptable.
But if I run this on a concurrent manner, let's say 5 request at-a-time, it starts to slip, to 100ms... if I run this on 25 concurrent requests I start to see the slip in seconds and when I run above 35 the slip is superior to 10 seconds (at which point I increased the sleep to 60s because the service was returning before the framework could "notice" that it had timeout)
Can anyone tell me what's going on? why is this "slip" happening at such a degree? is there a more reliable implementation for what I'm trying to achieve?
Details:
The service is very simple:
public string GetData(int value)
{
Console.WriteLine("Will sleep for " + value);
Thread.Sleep(value);
return string.Format("Slept for: {0}ms", value);
}
EDIT 1
I also tested this scenario:
var st = Stopwatch.StartNew();
CancellationTokenSource src = new CancellationTokenSource(1000);
try
{
var responseTask = Task.Run(() =>
{
var task = new WaitingService.ServiceClient().GetDataAsync(sleep);
if (!task.Wait(1000,src.Token))
{
return null;
}
task.Wait(src.Token);
return task.Result;
});
await responseTask;
}
catch { }
return Ok("Awaited for " + st.ElapsedMilliseconds + "ms. Supposed to be " + sleep);
But I actually got worse results... the slip intensified...
EDIT 2:
the following implementation got FAR better results, with 50 concurrent request rarely exceeding 2 seconds!
var st = Stopwatch.StartNew();
try
{
var responseTask = Task.Run(async () =>
{
var task = new WaitingService.ServiceClient().GetDataAsync(sleep);
do
{
await Task.Delay(50);
}while (task.Status == TaskStatus.Running || st.ElapsedMilliseconds < 1000);
if (task.Status == TaskStatus.RanToCompletion)
{
return task.Result;
}
else { return null; }
});
await responseTask;
}
catch { }
return Ok("Awaited for " + st.ElapsedMilliseconds + "ms. Supposed to be " + sleep);