【发布时间】:2017-08-29 15:02:40
【问题描述】:
我对一些代码有一个令人沮丧的问题,不知道为什么会出现这个问题。
//
// .NET FRAMEWORK v4.6.2 Console App
static void Main( string[] args )
{
var list = new List<string>{ "aa", "bbb", "cccccc", "dddddddd", "eeeeeeeeeeeeeeee", "fffff", "gg" };
foreach( var item in list )
{
Progress( item );
}
}
private static int _cursorLeft = -1;
private static int _cursorTop = -1;
public static void Progress( string value = null )
{
lock( Console.Out )
{
if( !string.IsNullOrEmpty( value ) )
{
Console.Write( value );
var left = Console.CursorLeft;
var top = Console.CursorTop;
Interlocked.Exchange( ref _cursorLeft, Console.CursorLeft );
Interlocked.Exchange( ref _cursorTop, Console.CursorTop );
Console.WriteLine();
Console.WriteLine( "Left: {0} _ {1}", _cursorLeft, left );
Console.WriteLine( "Top: {0} _ {1}", _cursorTop, top );
}
}
}
不运行 代码优化时,结果如预期。 _cursorLeft 和 left 就 _cursorTop 和 top 相等。
aa
Left: 2 _ 2
Top: 0 _ 0
bbb
Left: 3 _ 3
Top: 3 _ 3
但是当我运行它时 with 代码优化 两个值 _cursorLeft 和 _cursorTop 变得奇怪:
aa
Left: -65534 _ 2
Top: -65536 _ 0
bb
Left: -65533 _ 3
Top: -65533 _ 3
我发现了 2 个解决方法:
- 将 _cursorLeft 和 _cursorTop 设置为 0 而不是 -1
- 让 Interlocked.Exchange 从 left 分别取值。 顶部
因为解决方法 #1 不符合我的需求,我最终选择了解决方法 #2:
private static int _cursorLeft = -1;
private static int _cursorTop = -1;
public static void Progress( string value = null )
{
lock( Console.Out )
{
if( !string.IsNullOrEmpty( value ) )
{
Console.Write( value );
// OLD - does NOT work!
//Interlocked.Exchange( ref _cursorLeft, Console.CursorLeft );
//Interlocked.Exchange( ref _cursorTop, Console.CursorTop );
// NEW - works great!
var left = Console.CursorLeft;
var top = Console.CursorTop;
Interlocked.Exchange( ref _cursorLeft, left ); // new
Interlocked.Exchange( ref _cursorTop, top ); // new
}
}
}
但是这种奇怪的行为是从哪里来的呢?
是否有更好的解决方法/解决方案?
[由 Matthew Watson 编辑:添加简化再现:]
class Program
{
static void Main()
{
int actual = -1;
Interlocked.Exchange(ref actual, Test.AlwaysReturnsZero);
Console.WriteLine("Actual value: {0}, Expected 0", actual);
}
}
static class Test
{
static short zero;
public static int AlwaysReturnsZero => zero;
}
[由我编辑:]
我想出了另一个更短的例子:
class Program
{
private static int _intToExchange = -1;
private static short _innerShort = 2;
// [MethodImpl(MethodImplOptions.NoOptimization)]
static void Main( string[] args )
{
var oldValue = Interlocked.Exchange(ref _intToExchange, _innerShort);
Console.WriteLine( "It was: {0}", oldValue );
Console.WriteLine( "It is: {0}", _intToExchange );
Console.WriteLine( "Expected: {0}", _innerShort );
}
}
除非您不使用 优化 或将 _intToExchange 设置为 ushort 范围内的值,否则您不会发现问题。
【问题讨论】:
-
我可以重现这个。
-
我冒昧地添加了一个简化的复制品。您可以根据需要合并或删除它。
-
@MatthewWatson 好主意!我真的认为它必须是一个特定的问题,但它似乎是一个大错误。
-
以后应该会修复:github.com/dotnet/coreclr/issues/10714
标签: c# optimization release interlocked