Я пытаюсь реализовать базовый класс Future
(да, я знаю о Task
, но это для образовательных целей) и столкнулся с странным поведением Monitor
класса. Класс реализуется так, чтобы он входил в конструктор lock, и он запускает действие, которое завершает блокировку пула потоков. Результат getter проверяет переменную экземпляра, чтобы увидеть, завершено ли действие, а если нет, вводится блокировка и затем возвращается результат. Проблема в том, что на самом деле результат getter не ждет завершения действия в очереди и продолжения, что приводит к неправильным результатам. Вот код.Монитор, похоже, не блокирует объект
// The class itself
public class Future<T>
{
private readonly Func<T> _f;
private volatile bool _complete = false;
private T _result;
private Exception _error = new Exception("WTF");
private volatile bool _success = false;
private readonly ConcurrentStack<Action<T>> _callbacks = new ConcurrentStack<Action<T>>();
private readonly ConcurrentStack<Action<Exception>> _errbacks = new ConcurrentStack<Action<Exception>>();
private readonly object _lock = new object();
public Future(Func<T> f)
{
_f = f;
Monitor.Enter(_lock);
ThreadPool.QueueUserWorkItem(Run);
}
public void OnSuccess(Action<T> a)
{
_callbacks.Push(a);
if (_complete && _success)
a(_result);
}
public void OnError(Action<Exception> a)
{
_errbacks.Push(a);
if (_complete && !_success)
a(_error);
}
private void Run(object state)
{
try {
_result = _f();
_success = true;
_complete = true;
foreach (var cb in _callbacks) {
cb(_result);
}
} catch (Exception e) {
_error = e;
_complete = true;
foreach (var cb in _errbacks) {
cb(e);
}
} finally {
Monitor.Exit(_lock);
}
}
public T Result {
get {
if (!_complete) {
Monitor.Enter(_lock);
}
if (_success) {
return _result;
} else {
Console.WriteLine("Throwing error complete={0} success={1}", _complete, _success);
throw _error;
}
}
}
// Failing test
public void TestResultSuccess() {
var f = new Future<int>(() => 1);
var x = f.Result;
Assert.AreEqual (1, x);
}
Я использую Mono 3.2.3 на Mac OS X 10.9.
разные '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' на вашей стороне. Вам, похоже, нужен общий '_lock'. – Vlad
Есть ли у Mono 'Task'/'TaskCompletionSource '? 'cos, что было бы намного проще ... –
@ Vlad предположительно OP говорит о «в контексте одного будущего» ... –