У меня есть приложение WPF, которое время от времени должно выполнять длительные операции, или, скорее, многие небольшие операции, которые в общей сложности занимают время. Я обнаружил, что библиотека параллельных задач в .Net 4 прекрасно работает для этого.
Однако эта операция по своей природе должна завершиться до того, как будет запущена другая аналогичная операция И есть очень реальный шанс, что пользователь сможет выполнить действие, требующее запуска процесса, даже когда последний еще не завершен. Я хотел бы синхронизировать это так, чтобы одновременно работал только один. Когда запущенный экземпляр завершает работу, другой получает блокировки и использует их и т. Д. До тех пор, пока их больше не будет.
У меня есть класс, который запускает процесс с именем EntityUpdater. В этом классе я подумал, что было бы разумно определить объект синхронизации:
private static object _lockObject = new object();
Установка статичности должна гарантировать, что любой объект EntityUpdater будет ожидать своего поворота, пока блокировка правильная, верно?
Итак, моя наивная первая попытка сделала это перед запуском Задачи (которая, в свою очередь, запускает все другие маленькие дочерние задачи, привязанные к их родителю):
Monitor.Enter(_lockObject, ref _lockAquired);
(_ lockAquired - это просто локальный бул)
У главной задачи (у которой есть все дочерние задачи) есть продолжение, которое существует более или менее только для выполнения
Monitor.Exit(_lockObject);
Я знаю, что я должен поставить это в конце, но это в значительной степени единственный код в продолжении, поэтому я не понимаю, как это могло бы изменить ситуацию.
В любом случае, я предполагаю, что здесь есть некое многопоточное вуду, которое заставляет меня получить «Метод синхронизации объектов был вызван из несинхронизированного блока кода» Исключение SynchronizationLockException. Я убедился, что _lockAquired действительно соответствует действительности, и я попытался выполнить Monitor.Enter в нескольких разных местах, но я всегда получаю это.
Итак, в основном, мой вопрос заключается в том, как я могу синхронизировать доступ к объекту (сам объект не важен), чтобы в любой момент времени работала только одна копия процесса, и любые другие, которые могут быть запущены, пока один уже работает будет блокировать? Сложность - я полагаю - появляется с дополнительным требованием, чтобы блокировка была снята в будущем, когда все дочерние задачи первой задачи TPL будут завершены.
UPDATE
Вот код, который показывает, что я сейчас делаю.
public class EntityUpdater
{
#region Fields
private static object _lockObject = new object();
private bool _lockAquired;
private Stopwatch stopWatch;
#endregion
public void RunProcess(IEnumerable<Action<IEntity>> process, IEnumerable<IEntity> entities)
{
stopWatch = new Stopwatch();
var processList = process.ToList();
Monitor.Enter(_lockObject, ref _lockAquired);
//stopWatch.Start();
var task = Task.Factory.StartNew(() => ProcessTask(processList, entities), TaskCreationOptions.LongRunning);
task.ContinueWith(t =>
{
if(_lockAquired)
Monitor.Exit(_lockObject);
//stopWatch.Stop();
});
}
private void ProcessTask(List<Action<IEntity>> process, IEnumerable<IEntity> entities)
{
foreach (var entity in entities)
{
var switcheroo = entity; // To avoid closure or whatever
Task.Factory.StartNew(() => RunSingleEntityProcess(process, switcheroo), TaskCreationOptions.AttachedToParent);
}
}
private void RunSingleEntityProcess(List<Action<IEntity>> process, IEntity entity)
{
foreach (var step in process)
{
step(entity);
}
}
}
Как вы можете видеть, это не сложно, и это, вероятно, также далеко от достойного производства - просто попытка показать, что я не могу заставить работать.
Исключение, которое я получаю, конечно же, в вызове Monitor.Exit () в продолжении задачи.
Надеюсь, это прояснит ситуацию.