c# - System.Timers.Timer Elapsed 在使用 Task.Run with async from Console App 时间歇性地不触发

标签 c# multithreading async-await task-parallel-library

我正在使用一个控制台应用程序,我有一批 20 个 URI,我需要从中读取,我发现通过执行所有任务并并行运行它们然后在不同线程中完成后对结果进行排序(允许获取下一批)。

在我当前使用的调用中,每个线程在获取响应流时都会阻塞,我还看到了相同方法 GetResponseAsync 的异步版本。

我知道使用异步释放线程池有好处 Await and Async in same line而不是阻塞:

异步版本

return Task.Run(async () =>
{
    var uri = item.Links.Alternate();
    var request = (HttpWebRequest)WebRequest.Create(uri);

    var response = await request.GetResponseAsync();
    var stream = response.GetResponseStream();
    if (stream == null) return null;
    var reader = new StreamReader(stream);
    return new FetchItemTaskResult(reader.ReadToEnd(), index, uri);
});

屏蔽版本

return Task<FetchItemTaskResult>.Factory.StartNew(() =>
{
    var uri = item.Links.Alternate();
    var request = (HttpWebRequest)WebRequest.Create(uri);

    var response = request.GetResponse();
    var stream = response.GetResponseStream();
    if (stream == null) return null;
    var reader = new StreamReader(stream);
    return new FetchItemTaskResult(reader.ReadToEnd(), index, uri);
});

但是,我在异步版本的控制台应用程序上看到奇怪的暂停,其中 System.Timers.Timer elapsed 事件停止被调用很多秒(当它应该每秒关闭时)。

阻塞程序以每秒大约 3,500 个项目的速度运行,所有内核的 CPU 使用率约为 30%。

async on 以每秒大约 3,800 个事件的速度运行,CPU 使用率略高于阻塞但不多(仅 5%)...但是我使用的计时器似乎暂停了大约 10 到 15 秒大约每分钟一次,在我的 Main() 函数中:

private static void Main(string[] args)
{  
    // snip some code that runs the tasks

    var timer = new System.Timers.Timer(1000);

    timer.Elapsed += (source, e) =>
    {
        Console.WriteLine(DateTime.UtcNow);

        // snip non relevant code
        Console.WriteLine("Commands processed: " + commandsProcessed.Sum(s => s.Value) + " (" + logger.CommandsPerSecond() + " per second)");
    };
    timer.Start();
    Console.ReadKey();
}

那么定时器和线程池似乎在使用异步时有某种关联(并且只有异步,阻塞时没有暂停),或者可能没有,无论哪种方式,请问发生了什么以及如何进一步诊断?

最佳答案

the timer and thread pool are some how related

您的猜测是正确的。发生的事情基本上被称为臭名昭著的线程饥饿,即您的所有线程都很忙,因此ThreadPool 将没有足够的线程来运行事件委托(delegate)。

一旦在 ASP.NET 中运行,autoConfig="True"确保您获得足够的线程(在峰值情况下并不总是如此),并确保您不受连接限制的约束。但在控制台应用程序中,您必须自己执行此操作。

所以只需添加此代码段,我敢打赌您的问题就会消失:

ThreadPool.SetMinThreads(100, 100);
ServicePointManager.DefaultConnectionLimit = 1000;

关于c# - System.Timers.Timer Elapsed 在使用 Task.Run with async from Console App 时间歇性地不触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31663672/

相关文章:

c# - 绘制不同粗细的线

python - 在单独的线程中运行 Tkinter 表单

c# - 我可以在 IProgress.Report 上等待吗

c# - Entity Framework (EF6) 插入或更新属性(如果存在)

c# - 级联下拉列表数据源

session - session Bean中的线程

java - 以单线程方式包装回调函数

c# - 使用 CaSTLe Windsor 的异步 MVC 操作

python - Python 中的协程与 Lua 中的协程相比如何?

c# - 本地化日期验证器