Changeset 15452


Ignore:
Timestamp:
11/06/17 16:38:36 (13 months ago)
Author:
pfleck
Message:

#2822

  • Removed ManualResetEventSlim allOptimizerFinished.
  • Reworked execution state transitions into simple, documented rules
  • Fixed potentiall NullReferenceException in ExperimentView
Location:
trunk/sources
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Optimization.Views/3.3/ExperimentView.cs

    r15409 r15452  
    7878    #region Events
    7979    private void workersNumericUpDown_ValueChanged(object sender, System.EventArgs e) {
    80       Content.NumberOfWorkers = (int)workersNumericUpDown.Value;
     80      if (Content != null)
     81        Content.NumberOfWorkers = (int)workersNumericUpDown.Value;
    8182    }
    8283    #endregion
  • trunk/sources/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs

    r15408 r15452  
    122122    private bool experimentStopped = false;
    123123
    124     private readonly ManualResetEventSlim allOptimizerFinished = new ManualResetEventSlim(false); // this indicates that all started optimizers have been paused or stopped
     124    // track already started optimizers (.StartAsync does not set the executionstate immediately)
     125    // and to avoid restarting optimizers that were manually paused/stopped by the user
     126    private readonly IDictionary<IOptimizer, Task> startedOptimizers = new Dictionary<IOptimizer, Task>();
     127    private IEnumerable<IOptimizer> StartableOptimizers {
     128      get {
     129        return Optimizers
     130          .Where(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused)
     131          .Where(o => !startedOptimizers.ContainsKey(o));  // all startable optimizers that were not startet yet
     132      }
     133    }
    125134
    126135    public Experiment()
     
    205214        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
    206215
    207       // Multiple enumerations of runnableOptimizers is done on purpose to get the latest list of runnnable optimizers lazily
    208       var runnableOptimizers = Optimizers.Where(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused);
    209       if (!runnableOptimizers.Any()) return;
     216      startedOptimizers.Clear();
     217      if (!StartableOptimizers.Any()) return;
    210218
    211219      experimentStarted = true;
    212220      experimentStopped = false;
    213221
    214       var startedOptimizers = new Dictionary<IOptimizer, Task>(); // track already started optimizers (.StartAsync does not set the executionstate immediately)
    215222      using (var availableWorkers = new SemaphoreSlim(NumberOfWorkers, NumberOfWorkers)) {
    216         while (runnableOptimizers.Any(o => !startedOptimizers.ContainsKey(o))) {
     223        while (StartableOptimizers.Any()) {
    217224          try {
    218225            availableWorkers.Wait(cancellationToken);
    219             if (experimentStopped || !experimentStarted) break;
    220             var optimizer = runnableOptimizers.FirstOrDefault(o => !startedOptimizers.ContainsKey(o));
    221             if (optimizer != null) {
    222               var startedTask = optimizer.StartAsync(cancellationToken).ContinueWith(async t => {
    223                 availableWorkers.Release(); // is guaranteed to be not disposed yet because Task.WaitAll blocks before the end of the using
    224                 await t; // trigger a potential exception on the optimizerTask
    225               });
    226               startedOptimizers.Add(optimizer, startedTask.Unwrap()); // unwrap task because lambda of .ContinueWith is async
    227             }
     226            var optimizer = StartableOptimizers.FirstOrDefault();
     227            if (experimentStopped || !experimentStarted || optimizer == null) break;
     228
     229            var startedTask = optimizer.StartAsync(cancellationToken).ContinueWith(async t => {
     230              availableWorkers.Release(); // is guaranteed to be not disposed yet because Task.WaitAll blocks before the end of the using
     231              await t; // trigger a potential exception on the optimizerTask
     232            });
     233            startedOptimizers.Add(optimizer, startedTask.Unwrap()); // unwrap task because lambda of .ContinueWith is async
    228234          } catch (InvalidOperationException) { } catch (OperationCanceledException) { }
    229235        }
    230236
    231237        Task.WaitAll(startedOptimizers.Values.ToArray()); // retreive exeptions of the asyncrounously started optimizer
    232         allOptimizerFinished.Wait();
    233238      }
    234239    }
     
    297302    private void OnPaused() {
    298303      ExecutionState = ExecutionState.Paused;
    299       allOptimizerFinished.Set();
    300304      EventHandler handler = Paused;
    301305      if (handler != null) handler(this, EventArgs.Empty);
     
    304308    private void OnStopped() {
    305309      ExecutionState = ExecutionState.Stopped;
    306       allOptimizerFinished.Set();
    307310      EventHandler handler = Stopped;
    308311      if (handler != null) handler(this, EventArgs.Empty);
     
    400403    }
    401404    private void optimizer_Paused(object sender, EventArgs e) {
    402       lock (locker) {
    403         if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) {
    404           OnPaused();
    405         }
    406         experimentStarted = false;
    407       }
     405      UpdateExecutionState();
    408406    }
    409407    private void optimizer_Prepared(object sender, EventArgs e) {
    410       lock (locker)
    411         if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
     408      UpdateExecutionState();
    412409    }
    413410    private void optimizer_Started(object sender, EventArgs e) {
     
    416413    }
    417414    private void optimizer_Stopped(object sender, EventArgs e) {
     415      UpdateExecutionState();
     416    }
     417    private void UpdateExecutionState() {
     418      // Execution states of the Experiment are determined using the following _basic_ rules:
     419      //   if any Optimizer is Started      => Experiment is Started  (2. if)
     420      //   if any Optimizer is Paused       => Experiment is Paused   (3. if)
     421      //   if any Optimizer is Prepared     => Experiment is Prepared (5. if)
     422      //   else (all Optimizer are Stopped) => Experiment is Stopped  (6. if)
     423      // Additional there are two extra rules:
     424      //   if the Experiment is running and there are still optimizers that can be started => keep the Experiment Running (1. if)
     425      //   if experiment-stop is pending: Stop Experiment even if there are still Prepared Optimizer               (4. if)
     426
    418427      lock (locker) {
    419         if (experimentStopped) {
    420           if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
    421         } else {
    422           if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) return;
    423           else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) {
    424             OnStopped();
    425           } else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) {
    426             OnPaused();
    427           }
    428         }
    429       }
    430     }
     428        // 1. experiment is running & further startable optimizers are available => continue executing
     429        if (experimentStarted && StartableOptimizers.Any()) return;
     430
     431        // 2. any optimizer is running => continue executing
     432        else if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Started))
     433          return;
     434
     435        // 3. any optimizer is paused => experiment paused
     436        else if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Paused))
     437          OnPaused();
     438
     439        // 4. stop pending & all optimizers either stopped or prepared => experiment stopped
     440        else if (experimentStopped)
     441          OnStopped();
     442
     443        // 5. any optimizer prepared => experiment prepared
     444        else if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Prepared))
     445          OnPrepared();
     446
     447        // 6. (else) all optimizers stopped
     448        else
     449          OnStopped();
     450      }
     451    }
     452
    431453    private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
    432454      lock (runsLocker) {
Note: See TracChangeset for help on using the changeset viewer.