【发布时间】:2020-05-03 15:53:13
【问题描述】:
我在枚举IAsyncEnumerable 时遇到了一个奇怪的问题,并附有System.Linq.Async 运算符Take。在我的迭代器中,我有一个 try-finally 块,其中在 try 块内产生了一些值,在 finally 块内产生了一些清理代码。清理代码位于 lock 块内。问题是lock 块后面的任何代码都不会执行。没有抛出异常,只是忽略了代码,就像它不存在一样。 Here is a program 重现此行为:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class Program
{
static async Task Main()
{
await foreach (var item in GetStream().Take(1))
{
Console.WriteLine($"Received: {item}");
}
Console.WriteLine($"Done");
}
static async IAsyncEnumerable<int> GetStream()
{
var locker = new object();
await Task.Delay(100);
try
{
yield return 1;
yield return 2;
}
finally
{
Console.WriteLine($"Finally before lock");
lock (locker) { /* Clean up */ }
Console.WriteLine($"Finally after lock");
}
}
}
输出:
Received: 1
Finally before lock
Done
文本“Finally after lock”没有打印在控制台中!
这只发生在附加了Take 运算符的情况下。如果没有操作符,文本将按预期打印。
这是 System.Linq.Async 库中的错误、C# 编译器中的错误还是其他什么?
作为一种解决方法,我目前在finally 中使用嵌套的try-finally 块,它可以工作但很尴尬:
finally
{
try
{
lock (locker) { /* Clean up */ }
}
finally
{
Console.WriteLine($"Finally after lock");
}
}
.NET Core 3.1.3、.NET Framework 4.8.4150.0、C# 8、System.Linq.Async 4.1.1、Visual Studio 16.5.4、控制台应用程序
【问题讨论】:
-
非常有趣的行为。锁内的代码被执行;
Take(2)也会发生同样的事情;对于Take(3),发出"Finally after lock"。 -
你应该在github.com/dotnet/reactive报告它
-
小问题。为什么需要锁在那里?
-
@GuruStron 因为我在枚举开始时在集合中添加了一个对象,并在枚举完成时将其删除。而使用
lock是确保线程安全的最简单方法。 -
@TheodorZoulias 我有点困惑,因为锁定对象是 repro =) 方法的本地对象。也有待讨论,但我个人认为使用concurrent collection 是最简单的)
标签: c# async-await iasyncenumerable linq-async