J'ai un problème frustrant avec un peu de code et je ne sais pas pourquoi ce problème se produit.L'optimisation du code C# provoque des problèmes avec Interlocked.Exchange()
//
// .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);
}
}
}
Lors de l'exécution sansOptimisation du code alors le résultat est comme prévu. _cursorLeft et gauche aussi loin que _cursorTop et top sont égaux.
aa
Left: 2 _ 2
Top: 0 _ 0
bbb
Left: 3 _ 3
Top: 3 _ 3
Mais quand je le lance avecOptimisation du code les valeurs _cursorLeft et _cursorTop deviennent bizzare:
aa
Left: -65534 _ 2
Top: -65536 _ 0
bb
Left: -65533 _ 3
Top: -65533 _ 3
J'ai trouvé 2 solutions de contournement:
- définir _cursorLeft et _cursorTop à au lieu de -1
- laisser Interlocked.Exchange prendre la valeur de gauche resp. top
Parce que solution de contournement # 1 ne correspond pas à mes besoins j'ai fini avec solution de contournement # 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
}
}
}
Mais d'où vient ce comportement bizarre vient?
Et y a-t-il une meilleure solution de contournement/solution?
[Modifier par Matthew Watson: Ajout repro simplifiée:]
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;
}
[Modifier par moi:]
je me suis dit un autre exemple encore plus court:
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);
}
}
À moins que vous n'utilisiez pas Optimisation ou définir _intToExchange à une valeur dans la plage de ushort
vous ne reconnaîtriez pas le problème.
Je peux reproduire ceci. –
Je me suis permis d'ajouter une repro simplifiée. Vous pouvez l'incorporer ou l'effacer comme bon vous semble. –
@MatthewWatson Bonne idée! Je pensais vraiment que cela devait être un problème spécifique, mais cela semble être un gros problème. – Ronin