У меня есть неприятная проблема с небольшим количеством кода и не знаю, почему эта проблема возникает.
//
// .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 и оставлены до тех пор, пока _cursorTop и top равны.
aa
Left: 2 _ 2
Top: 0 _ 0
bbb
Left: 3 _ 3
Top: 3 _ 3
Но когда я запускаю его с оптимизацией кода, оба значения _cursorLeft и _cursorTop становятся bizzare:
aa
Left: -65534 _ 2
Top: -65536 _ 0
bb
Left: -65533 _ 3
Top: -65533 _ 3
Я обнаружил 2 обходных пути:
- установите _cursorLeft и _cursorTop в 0 вместо -1
- пусть Interlocked.Exchange принимает значение слева или. сверху
Поскольку обходной путь №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
}
}
}
Но откуда это странное поведение?
И есть ли лучшее решение/решение?
[Редактировать Мэтью Уотсон: добавление упрощенного воспроизведения:]
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
, вы не узнаете проблему.