【问题标题】:Threading in .NET core ASP Controllers.NET 核心 ASP 控制器中的线程
【发布时间】:2020-02-18 10:46:27
【问题描述】:

我已经制作了一个干净的 .net Core 3.0 Web 应用程序项目,我正在尝试了解 ThreadPool 在 C# 中的工作原理。

namespace TestASPSelf.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public static int countThread = 0;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {

            int workerThreads;
            int portThreads;

            ThreadPool.GetMaxThreads(out workerThreads, out portThreads);
            Console.WriteLine("\nMaximum worker threads: \t{0}" +
                              "\nMaximum completion port threads: {1}",
                workerThreads, portThreads);

            ThreadPool.GetAvailableThreads(out workerThreads,
                out portThreads);
            Console.WriteLine("\nAvailable worker threads: \t{0}" +
                              "\nAvailable completion port threads: {1}\n",
                workerThreads, portThreads);    

                Console.WriteLine("countThread = " + countThread);

            return View();
        }

        class Z
        {
            public static void WaitTest(object o)
            {
                countThread++;
                while (true)
                {
                    Thread.Sleep(1000);
                }
            }
        }

        public IActionResult Privacy()
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("starting thread "+i);
                ThreadPool.QueueUserWorkItem(new WaitCallback(Z.WaitTest));
            }

            return View();
        }
}
}

http://localhost:5000/Home/Privacy 打开时,它会挂起一段时间(大约 40-80 秒),但我知道,for 循环的逻辑几乎立即完成。 当http://localhost:5000/在那之后打开时,它也会挂起40-80秒,结果在控制台countThread = 100中。 启动线程时,应用的 CPU 使用率约为 5-10%。

我正在努力理解:

1) 第一个是为什么 ASP 控制器每页挂起 40-80 秒,当 100 个线程正在运行时,CPU 使用率为 5-10%。 CPU有很多资源,RAM也是空闲的,但是为什么页面的ASP Controller方法挂了?

2) 如何在 C# 中创建运行线程数有限的 ThreadPool?如果我正确理解方法public static bool SetMinThreads (int workerThreads, int completionPortThreads);,它会影响应用程序的全局所有线程。如何创建活动线程数有限的 ThreadPool 对象,如 Java 中的ExecutorService?例如,线程池的 Java 代码可能如下所示

ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
          }
        executor.shutdown();
        while (!executor.isTerminated()) {
        }

3) 如何防止 ASP 控制器的所有方法挂起并创建“真正的”线程,就像在 Java 中一样?

【问题讨论】:

  • 用 C# 开发时不要用 Java 思考。要实现所需的行为,有几种方法。忘记你对 Java 的了解,看看如何在 C# 中实现你想要的。例如,您可以自己启动 n 个线程,您可以使用设置了 maxParallelism 的 DataFlow,仅举个例子 2。
  • asp.net 本身就是一个多线程环境:每个请求都有自己的线程
  • 致 Fildor:结果,我使用本教程解决了任务 - docs.microsoft.com/en-us/dotnet/standard/parallel-programming/…。感谢您使用 DataFlow 的想法。能否请您写一个关于 DataFlow 的答案,我会将答案标记为正确?

标签: c# asp.net multithreading .net-core


【解决方案1】:
ThreadPool.QueueUserWorkItem(new WaitCallback(Z.WaitTest));

这样你就做错了。您导致线程池中的线程阻塞,因此池无法完成处理您的请求并处理新请求。

在某个时候,池中的一个线程设法返回并处理您的下一个请求,但由于池过载,它再次挂起。

至于您的其他问题,请说明您想要实现的目标。您的问题似乎试图解决一个没有被很好理解的问题。


更新:在亚瑟的评论之后。

如果你要下载文件,你应该使用 Tasks 和 async-await。 IO 操作不消耗线程(更多here)。

创建N个任务,每个任务下载一个文件,然后等待Task.WhenAll。伪代码:

List<Task> tasks = new List<Task>();
for (int i = 0; i < filesToDownloadCount; i++)
{
   var t = new Task ( () => { /* ... code to download your file here ... */});
   tasks.Add (t);
}
await t.WhenAll (tasks);

这种方法将为您提供最佳吞吐量,您的瓶颈将是您的带宽,而不是 CPU。

【讨论】:

  • 感谢您的回答。在我的场景中,我使用线程来发出网络请求,而不是 CPU 密集型。我知道,ThreadPool 一次可以运行大约 32000 个线程,但我需要一次将请求最多限制为 4 个线程。所以,public IActionResult Privacy() 初始化了一个线程池,每个线程都是一个下载文件的方法。文件列表是动态的,我需要像ExecutorService executor = Executors.newFixedThreadPool(5); 这样的对象随时将线程传递给它,因为找到了文件链接。
  • 每个线程都有成本 - 至少 1 MB 的堆栈。运行 32K 线程意味着失去 32GB RAM。我更新了答案以反映您的评论。
【解决方案2】:

ThreadPool 类有几个静态方法,包括 QueueUserWorkItem,它负责在线程池工作线程可用时调用它。如果线程池中没有可用的工作线程,则等待直到线程可用。

using System;  
using System.Threading;  

class ThreadPoolSample  
{  
    // Background task   
    static void BackgroundTask(Object stateInfo)  
    {  
        Console.WriteLine("Hello! I'm a worker from ThreadPool");  
        Thread.Sleep(1000);          
    }  

    static void BackgroundTaskWithObject(Object stateInfo)  
    {  
        Person data = (Person)stateInfo;          
        Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
        Thread.Sleep(1000);  
    }  
    static void Main(string[] args)  
    {  
        // Use ThreadPool for a worker thread        
        ThreadPool.QueueUserWorkItem(BackgroundTask);  
        Console.WriteLine("Main thread does some work, then sleeps.");  
        Thread.Sleep(500);  

        // Create an object and pass it to ThreadPool worker thread  
        Person p = new Person("Mahesh Chand", 40, "Male");  
        ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  

        int workers, ports;  

        // Get maximum number of threads  
        ThreadPool.GetMaxThreads(out workers, out ports);  
        Console.WriteLine($"Maximum worker threads: {workers} ");  
        Console.WriteLine($"Maximum completion port threads: {ports}");  

        // Get available threads  
        ThreadPool.GetAvailableThreads(out workers, out ports);  
        Console.WriteLine($"Availalbe worker threads: {workers} ");  
        Console.WriteLine($"Available completion port threads: {ports}");  

        // Set minimum threads  
        int minWorker, minIOC;  
        ThreadPool.GetMinThreads(out minWorker, out minIOC);  
        ThreadPool.SetMinThreads(4, minIOC);  

        // Get total number of processes availalbe on the machine  
        int processCount = Environment.ProcessorCount;  
        Console.WriteLine($"No. of processes available on the system: {processCount}");  

        // Get minimum number of threads  
        ThreadPool.GetMinThreads(out workers, out ports);  
        Console.WriteLine($"Minimum worker threads: {workers} ");  
        Console.WriteLine($"Minimum completion port threads: {ports}");  

        Console.ReadKey();  
    }  

        // Create a Person class  
        public class Person  
        {  
            public string Name { get; set; }  
            public int Age { get; set; }  
            public string Sex { get; set; }  

            public Person(string name, int age, string sex)  
            {  
                this.Name = name;  
                this.Age = age;  
                this.Sex = sex;  
            }  
        }  
}  

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多