I am trying to load a queue of interval processes. In other words I have a queue and I want each item in the queue to run on an individual interval.
My problem is that I can't seem to get more than 25 threads to run at one time. I'm using .Net 4.5 on a 64bit machine which has a default max thread count of 32768.
How do I make my app run as many concurrent threads as my machine can handle?
Here is an example app that replicates the actual problem in my production code:
class Program
{
static void Main(string[] args)
{
System.Threading.ThreadPool.SetMaxThreads(200, 200);
test t = new test();
t.LoadUrls("http://www.google.com");
while (1 == 1)
{
System.Threading.Thread.Sleep(1000);//refresh every 5 seconds
Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
}
}
public class test
{
public void LoadUrls(string url)
{
for (int i = 0; i < 100; i++)
{
System.Threading.Timer t = new System.Threading.Timer(new System.Threading.TimerCallback(RunInterval), url, 0, 1000);
Console.WriteLine("Loaded {0} feeds.", i);
}
}
private static void RunInterval(object state)
{
string url = state as string;
string data = "";
using (System.Net.WebClient cl = new System.Net.WebClient())
{
Console.WriteLine("getting data for " + url);
data = cl.DownloadString(url);
}
//do something with the data
}
}
}
This code should theoretically run 198 threads after 2 seconds or so.
By the way, this worked beautifully in my prototype app; it was written in node. But, now I can't get it to work correctly in c#...
ANSWER: The problem was actually with garbage collection and wasn't a threadpool issue at all; the pool is more than capable of spooling all the threads I'm throwing at it. The trick is to use the single parameter constructor of the System.Threading.Timer; this will make the timer use itself as a semaphore, thus avoiding gc.
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
test t = new test();
t.url = "http://www.google.com?" + i;
System.Threading.Timer ti = new System.Threading.Timer(new System.Threading.TimerCallback(t.RunInterval));
ti.Change(0, 1000);
}
while (1 == 1)
System.Threading.Thread.Sleep(int.MaxValue);
}
public class test
{
public string url { get; set; }
public void RunInterval(object state)
{
Console.WriteLine("getting data for " + this.url);
string data = "";
using (System.Net.WebClient cl = new System.Net.WebClient())
{
data = cl.DownloadString(this.url);
}
}
}
}
I'm not for sure why you would ever want a timer to be collected by the gc, but hey what do I know.