我需要担心阻塞任务吗

本文关键字:任务 担心 | 更新日期: 2023-09-27 18:01:03

在.NET中阻塞任务需要担心多少?即.NET任务调度程序如何处理线程池中线程的阻塞和超额订阅?

例如,如果我在任务中有一些IO,我是否应该始终使用LongRunning提示创建它?或者任务调度程序启发式算法处理得更好?在C++中有一个Oversubscribe提示,它运行得很好,但我在.NET中没有找到任何等效的提示。

我需要担心阻塞任务吗

ThreadPool确实检测到它的一个线程何时阻塞,这是向池中添加另一个线程的提示。因此,如果你阻塞了很多,性能很可能不会很糟糕,因为ThreadPool会试图让你的CPU核心保持繁忙。

但是,有许多被阻塞的线程可能是一个性能问题,因为这会增加内存消耗,并可能导致更多的上下文切换。

此外,这种行为可能会导致IO性能下降。对于旋转磁盘(HDD(,同时访问许多文件会导致大量查找,这可能会严重影响性能。

如果您想要性能最好的代码,您确实需要担心它。

处理它的最好方法是使用.Net 4.5"等待"风格的I/O。

如果你没有.Net 4.5,你将不得不使用旧风格的I/O(它同样有效,但更难使用(。

这些文章中描述的非阻塞I/O是迄今为止使用多线程进行I/O的最佳方式。

如果您没有使用I/O,那么您可能仍然可以从这些文章中学到很多东西。

LongRunning向TPL发出信号,而不是使用线程池线程——它创建一个非线程池线程来完成请求(例如new Thread(...)(。这是而不是你应该为IO做的事情。你应该使用异步IO。例如:

using(var response = (HttpWebResponse)await WebRequest.Create(url).GetResponseAsync())
    return response.StatusCode == HttpStatusCode.OK;

这确保了在任何可能的情况下使用重叠的IO,即使用IO线程池。

如果要将Task与遗留的APM API一起使用,则可以使用FromAsync:

Task<int> bytesRead = Task<int>.Factory.FromAsync( 
    stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null);
await bytesRead;

如果您需要处理遗留事件异步API,您可以使用TaskCompletionSource:

TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
WebClient[] webClients = new WebClient[urls.Length];
object m_lock = new object();
int count = 0;
List<string> results = new List<string>();
for (int i = 0; i < urls.Length; i++)
{
    webClients[i] = new WebClient();
    // Specify the callback for the DownloadStringCompleted 
    // event that will be raised by this WebClient instance.
    webClients[i].DownloadStringCompleted += (obj, args) =>
    {
        // Argument validation and exception handling omitted for brevity. 
        // Split the string into an array of words, 
        // then count the number of elements that match 
        // the search term. 
        string[] words = args.Result.Split(' ');
        string NAME = name.ToUpper();
        int nameCount = (from word in words.AsParallel()
                         where word.ToUpper().Contains(NAME)
                         select word)
                        .Count();
        // Associate the results with the url, and add new string to the array that  
        // the underlying Task object will return in its Result property.
        results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
        // If this is the last async operation to complete, 
        // then set the Result property on the underlying Task. 
        lock (m_lock)
        {
            count++;
            if (count == urls.Length)
            {
                tcs.TrySetResult(results.ToArray());
            }
        }
    };
    // Call DownloadStringAsync for each URL.
    Uri address = null;
    address = new Uri(urls[i]);
    webClients[i].DownloadStringAsync(address, address);
} // end for 
await tcs.Task;