并行使用 ILogger
Using ILogger in parallel
在以下代码中,程序在日志完全刷新之前终止:
class Program
{
static void Main(string[] args)
{
var count = -1;
var tasks = new ConcurrentBag<Task>();
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();
if (logger is null)
throw new NullReferenceException();
Parallel.ForEach(Enumerable.Range(0, 1000), i =>
{
tasks.Add(Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
//tasks.Add(Task.Run(() => Console.WriteLine(Interlocked.Increment(ref count).ToString())));
});
Task.WhenAll(tasks).Wait();
}
}
尽管应该打印 1.000 条日志语句,但只有以下内容被刷新:
info: Net5.Program[0]
6
info: Net5.Program[0]
2
info: Net5.Program[0]
5
info: Net5.Program[0]
1
info: Net5.Program[0]
7
info: Net5.Program[0]
4
info: Net5.Program[0]
3
info: Net5.Program[0]
0
info: Net5.Program[0]
8
info: Net5.Program[0]
10
C:\Users\MyName\source\repos\ConsoleApp\Net5\bin\Debug\net5.0\Net5.exe (process 28048) exited with code 0.
Press any key to close this window . . .
当使用Console.WriteLine
时(在上面的代码中被注释掉),然而,所有行都被打印出来,这导致怀疑控制台记录器在幕后是异步的。
如何确保在不诉诸手动延迟的情况下等待所有日志语句?
编辑:
我选择了以下代码,它成功了:
class Program
{
static void Main(string[] args)
{
var count = -1;
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
using var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
var tasks = Enumerable.Range(0, 50000)
.AsParallel()
.Select(_ => Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
Task.WhenAll(tasks).Wait();
}
}
主要区别:
- Service Provider 已处理,修复了日志刷新问题。
- 使用
GetRequiredService
获取 ILogger 实例以避免必须进行空检查。
- 使用
Enumerable.Range(...).AsParallel.Select(...)
而不是 Parallel.ForEach
来坚持使用任务。
如果您销毁服务提供者,它将销毁所有单例服务并刷新记录器。
也没有必要使用Parallel.ForEach
。事实上,将 Parallel.ForEach
与 async
`await` 一起使用是一种代码味道。几乎总是错的。
而且,由于您标记了问题 .NET 5.0
,您可以将代码更改为:
class Program
{
static async Task Main()
{
var count = -1;
var tasks = new ConcurrentBag<Task>();
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
using var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
for (var i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
};
await Task.WhenAll(tasks);
}
}
在以下代码中,程序在日志完全刷新之前终止:
class Program
{
static void Main(string[] args)
{
var count = -1;
var tasks = new ConcurrentBag<Task>();
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<Program>>();
if (logger is null)
throw new NullReferenceException();
Parallel.ForEach(Enumerable.Range(0, 1000), i =>
{
tasks.Add(Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
//tasks.Add(Task.Run(() => Console.WriteLine(Interlocked.Increment(ref count).ToString())));
});
Task.WhenAll(tasks).Wait();
}
}
尽管应该打印 1.000 条日志语句,但只有以下内容被刷新:
info: Net5.Program[0]
6
info: Net5.Program[0]
2
info: Net5.Program[0]
5
info: Net5.Program[0]
1
info: Net5.Program[0]
7
info: Net5.Program[0]
4
info: Net5.Program[0]
3
info: Net5.Program[0]
0
info: Net5.Program[0]
8
info: Net5.Program[0]
10
C:\Users\MyName\source\repos\ConsoleApp\Net5\bin\Debug\net5.0\Net5.exe (process 28048) exited with code 0.
Press any key to close this window . . .
当使用Console.WriteLine
时(在上面的代码中被注释掉),然而,所有行都被打印出来,这导致怀疑控制台记录器在幕后是异步的。
如何确保在不诉诸手动延迟的情况下等待所有日志语句?
编辑:
我选择了以下代码,它成功了:
class Program
{
static void Main(string[] args)
{
var count = -1;
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
using var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
var tasks = Enumerable.Range(0, 50000)
.AsParallel()
.Select(_ => Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
Task.WhenAll(tasks).Wait();
}
}
主要区别:
- Service Provider 已处理,修复了日志刷新问题。
- 使用
GetRequiredService
获取 ILogger 实例以避免必须进行空检查。 - 使用
Enumerable.Range(...).AsParallel.Select(...)
而不是Parallel.ForEach
来坚持使用任务。
如果您销毁服务提供者,它将销毁所有单例服务并刷新记录器。
也没有必要使用Parallel.ForEach
。事实上,将 Parallel.ForEach
与 async
`await` 一起使用是一种代码味道。几乎总是错的。
而且,由于您标记了问题 .NET 5.0
,您可以将代码更改为:
class Program
{
static async Task Main()
{
var count = -1;
var tasks = new ConcurrentBag<Task>();
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddConsole());
using var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
for (var i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(() => logger.LogInformation(Interlocked.Increment(ref count).ToString())));
};
await Task.WhenAll(tasks);
}
}