Last active
January 18, 2021 22:33
-
-
Save AqlaSolutions/f8c993bfad32dc9be11703fea4e01422 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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