3

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

例如,如果我在一个任务中有一些 IO,我应该总是用LongRunning提示创建它吗?或者任务调度器启发式处理它更好吗?在 C++ 中有一个Oversubscribe可以完美运行的提示,但我在 .NET 中没有找到任何等效项。

4

3 回答 3

2

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

处理它的最佳方法是使用.Net 4.5“等待”风格的 I/O

如果您没有 .Net 4.5,则必须使用较旧的 I/O 样式(效果一样好,但更难使用)。

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

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

于 2013-02-26T09:18:22.163 回答
2

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

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

此外,这种行为可能会导致 IO 性能下降。使用旋转磁盘 (HDD),同时访问多个文件会导致大量搜索,这会极大地影响性能。

于 2013-02-26T18:21:05.603 回答
1

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

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

这样可以确保尽可能使用重叠的 IO——它使用 IO 线程池。

如果您想使用带有旧版 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;
于 2013-02-26T18:26:22.340 回答