Ich entwickle einen kleinen Interpreter, der Anweisungen verarbeitet und Variablen speichert. Wenn ich den Interpreter in mehreren Threads ausführe (3 Threads zum Testen, in der Version werden es 10–30 Threads sein), stoße ich auf ein Problem, bei dem Variablen von anderen Threads überschrieben werden.
Problem: Wenn mehrere Threads den Interpreter ausführen, werden in einem Thread erstellte Variablen durch Variablen aus anderen Threads überschrieben. Beispielsweise zeigt die Ausgabe manchmal in allen Threads den gleichen Wert für myVar an, obwohl jeder Thread ihn unabhängig festlegen sollte.
Ich habe ThreadStatic für threadspezifische Daten verwendet, z. B InterpreterState-Klasse, aber dies scheint das Überschreiben der Variablen nicht zu verhindern und hat die Interpreter-Klasse threadsicher gemacht, indem InterpreterState pro Thread isoliert wurde, aber das Problem besteht weiterhin.
Hier ist der Code für die VariableStorage-Klasse, die ein ConcurrentDictionary mit Lazy verwendet (basierend auf diesem Artikel):
public class VariableItem // class of variable item
{
public string Name { get; set; } = string.Empty;
public object? Value { get; set; } = null;
public override bool Equals(object? obj)
{
if (obj is VariableItem item)
{
return Name == item.Name;
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(Name);
}
}
public class VariableStorage // variables storage
{
private readonly ConcurrentDictionary _storage = new();
public void AddOrUpdate(VariableItem item)
{
_storage.AddOrUpdate(item.Name,
new Lazy(() => item),
(key, oldValue) => new Lazy(() => item));
}
public bool Remove(VariableItem item)
{
return _storage.TryRemove(item.Name, out _);
}
public VariableItem? Get(string name)
{
if (_storage.TryGetValue(name, out var lazyItem))
{
return lazyItem.Value;
}
return null;
}
public VariableItem[] GetAll()
{
return _storage.Values.Select(lazyItem => lazyItem.Value).ToArray();
}
public void Clear()
{
_storage.Clear();
}
}
Und hier ist die Interpreter-Klasse:
public class Interpreter
{
private static readonly InterpreterState _state = new();
private static readonly VariableStorage _variables = new();
public Interpreter(string[] tokens, string type)
{
_state.Tokens = tokens;
}
public void Execute()
{
try
{
while (_state.Position < _state.Tokens.Length)
{
var (cmd, value) = ParseStatement().GetAwaiter().GetResult(); // run instructions parser
}
}
finally
{
_variables.Clear();
}
}
}
Wenn Variablen hinzugefügt oder aktualisiert werden, wird die AddOrUpdate-Methode in VariableStorage wie folgt aufgerufen:
_variables.AddOrUpdate(new VariableItem { Name = variableName, Value = variableValue });
Zum Schluss noch ein vereinfachtes Beispiel für die Multithread-Ausführung:
List threads = new();
for (int i = 0; i < 3; i++)
{
var thread = new Thread(() =>
{
var interpreter = new Interpreter(new[]
{
"SET(myVar, 'Value" + Thread.CurrentThread.ManagedThreadId + "')", // instruction to edit or create variable, now its creates myVar with value: 'Value{N}' where N is thread id
"PRINT(myVar)" // instruction to show in console variable value
}, "test");
interpreter.Execute();
});
threads.Add(thread);
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
Und hier ist das Ergebnis, das ich bekomme:
//this is variable creating
Variable myVar set to Value12 (System.String) // thread id 12
Variable myVar set to Value13 (System.String) // thread id 13
Variable myVar set to Value14 (System.String) // thread id 14
// this is shows value of variable `myVar`
Variable myVar value: Value14 (System.String) // thread id 12
Variable myVar value: Value14 (System.String) // thread id 13
Variable myVar value: Value14 (System.String) // thread id 14
Wie Sie sehen können, überschreibt der letzte Thread, der die Anweisung zum Bearbeiten oder Erstellen der Variablen myVar ausführt, diese mit dem zuletzt empfangenen Wert, aber so sollte es sein:
//this is variable creating
Variable myVar set to Value12 (System.String) // thread id 12
Variable myVar set to Value13 (System.String) // thread id 13
Variable myVar set to Value14 (System.String) // thread id 14
// this is shows value of variable `myVar`
Variable myVar value: Value12 (System.String) // thread id 12
Variable myVar value: Value13 (System.String) // thread id 13
Variable myVar value: Value14 (System.String) // thread id 14
Ich kann nicht verstehen, was ich falsch mache. Vielen Dank für Ihre Hilfe
Thread-Sicherheitsprobleme bei der Variablenspeicherung in einem benutzerdefinierten Interpreter, der auf mehreren Threa ⇐ C#
-
- Similar Topics
- Replies
- Views
- Last post