.NET最大并发计时器线程数

7
我正在尝试加载一个间隔进程的队列。换句话说,我有一个队列,希望队列中的每个项在单独的时间间隔上运行。
我的问题是,我似乎无法同时运行超过25个线程。我使用的是.NET 4.5,在一台默认最大线程数为32768的64位机器上。
如何使我的应用程序运行尽可能多的并发线程?
这里有一个示例应用程序,可以复制我生产代码中的实际问题:
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

        }
    }
}

这段代码理论上应该在大约2秒后运行198个线程。
顺便说一下,在我的原型应用程序中,这个代码完美地工作了;它是使用node编写的。但是,现在我无法在c#中正确地运行它...
答案: 问题实际上是垃圾回收造成的,而不是线程池问题;线程池足以处理我投入其中的所有线程。关键是要使用System.Threading.Timer的单参数构造函数;这会使计时器将自己用作信号量,从而避免 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);
            }
        }
    }
}

我不确定为什么你会希望计时器被垃圾回收收集,但是嘿,我也不知道。


1
你为什么想要同时运行这么多线程? - System Down
这是一个用于轮询服务的程序。它必须在指定的时间间隔内轮询n个源。我可以创建一个复杂的队列系统并运行n个单独的服务,每个服务只轮询1个源。但是,我想让它更加元化;我希望程序能够启动每个单独的进程。 - Eulalie367
你的答案仍然是错误的。所有东西都可以进行垃圾回收,没有特殊的排除定时器。你应该保留对所有定时器的引用,然后在完成后将它们处理掉。否则,它们可能会在垃圾回收时被处理掉。 - TimTIM Wong
3个回答

5
我该如何让我的应用程序运行尽可能多的并发线程?
这是错误的方法。每个线程都很昂贵,创建几百个会严重降低系统性能。
您的代码使用了线程池。该池具有限制线程数的算法。当您增加 Sleep() 时间时,您可能会看到更多的线程。
更直接的方法是设置 ThreadPool.MinThreads。但是请预期性能会降低,而不是提高。

此外,它从未使用超过我可用CPU的1%。 - Eulalie367
1
好奇你如何测量内存,每个线程应该使用1 MB。而Sleep()和I/O调用不使用CPU,这就是为什么你看到1%的原因。这是更多线程无法帮助的一个很好的指标。 - H H
只需删除Sleep()调用并在计时器事件中运行I/O即可。我不认为有直接干扰的原因。(测试时,Sleep()是一个糟糕的替代品)。 - H H
设置最小线程数可以增加线程数量,但仍然存在问题。System.Threading.ThreadPool.SetMinThreads(200, 200); Main函数中的Thread.Sleep()是否会使子线程也休眠?如果是,那么如何保持所有线程无限期地运行? - Eulalie367
如果您运行代码,它只会运行每个线程一次。换句话说,设置minthreads有效,但计时器从未重新触发。我想这可能是因为所有计时器都在现在正在休眠的主线程中。我将Main中的sleep(100)更改为console.readline,但结果相同。顺便说一下,感谢所有的帮助(我感到很傻,实际上必须发布一个问题到stackoverflow)。 - Eulalie367
显示剩余7条评论

3
根据System.Threading.Timer,
使用TimerCallback委托指定计时器要执行的方法。计时器构造时指定委托,无法更改。该方法不在创建计时器的线程上执行;它在系统提供的ThreadPool线程上执行。
然后在System.Threading.Threadpool中,
每个进程有一个线程池。从.NET Framework 4开始,进程的线程池默认大小取决于若干因素,例如虚拟地址空间的大小。进程可以调用GetMaxThreads方法来确定线程数。使用SetMaxThreads方法可更改线程池中的线程数。每个线程使用默认堆栈大小并以默认优先级运行。

1

MaxDegree设定了最大值,而不是最小值。 - H H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接