Skip to content

Instantly share code, notes, and snippets.

@AqlaSolutions
Last active January 18, 2021 22:33
Show Gist options
  • Save AqlaSolutions/f8c993bfad32dc9be11703fea4e01422 to your computer and use it in GitHub Desktop.
Save AqlaSolutions/f8c993bfad32dc9be11703fea4e01422 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DotNetThreadPoolAsyncProblem
{
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMinThreads(2, 200);
ThreadPool.SetMaxThreads(10, 200);
IConnection connection = new Connection(new User());
int waitingStart = 0, inProgress = 0;
int sleepTime = 150;
int waited = 0;
for (;;)
{
Interlocked.Increment(ref waitingStart);
// simulate client requests
// assume IConnection interface can't be changed to async
ThreadPool.QueueUserWorkItem(
_ =>
{
Interlocked.Decrement(ref waitingStart);
Interlocked.Increment(ref inProgress);
connection.OnRequestReceived("request");
Interlocked.Decrement(ref inProgress);
});
string msg = string.Format(
"Requests in queue: {0}, requests in progress: {1}, current sleep time: {2}",
Volatile.Read(ref waitingStart),
Volatile.Read(ref inProgress),
Volatile.Read(ref sleepTime));
bool poolAlive = CheckPoolAlive();
if (waited > 500 || !poolAlive)
{
Console.Clear();
Console.WriteLine(msg);
waited = 0;
}
if (!poolAlive)
throw new InvalidOperationException("ThreadPool deadlocked! " + msg);
if (sleepTime>30) sleepTime--;
Thread.Sleep(sleepTime);
waited += sleepTime;
}
}
static bool CheckPoolAlive()
{
// very simple check for ThreadPool deadlock
// during this check no new requests will be enqueued
// failing this check means that all current executing requests will never finish
using (ManualResetEventSlim ok = new ManualResetEventSlim(false))
{
ThreadPool.QueueUserWorkItem(_ => ok.Set());
return ok.Wait(30000);
}
}
interface IConnection
{
void OnRequestReceived(string request);
}
class Connection : IConnection
{
readonly User _user;
public Connection(User user)
{
_user = user;
}
public void OnRequestReceived(string request)
{
// assume we need request result synchronously (not callback in ContinueWith)
if (!_user.HandleRequest(request).Result) throw new InvalidOperationException();
}
}
class User
{
public async Task<bool> HandleRequest(string request)
{
await Task.Delay(Rnd.Next(30, 700)); // e.g. opening db connection may take some time
return true;
}
}
[ThreadStatic]
static Random _rnd;
static Random Rnd => _rnd ?? (_rnd = new Random(Guid.NewGuid().GetHashCode()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment