Index: stable/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs
===================================================================
--- stable/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs	(revision 15382)
+++ stable/HeuristicLab.Optimization/3.3/MetaOptimizers/BatchRun.cs	(revision 15573)
@@ -258,4 +258,5 @@
         if (ExecutionState == ExecutionState.Paused || ExecutionState == ExecutionState.Stopped) break;
         Optimizer.Prepare();
+        if (ExecutionState == ExecutionState.Paused || ExecutionState == ExecutionState.Stopped) break;
       }
     }
@@ -271,5 +272,5 @@
       if (Optimizer.ExecutionState != ExecutionState.Started) return;
       // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-      try { Optimizer.Pause(); } catch (InvalidOperationException) { }
+      try { Optimizer.Pause(); } catch (InvalidOperationException) { } catch (NotSupportedException) { }
     }
     public void Stop() {
Index: stable/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs
===================================================================
--- stable/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs	(revision 15382)
+++ stable/HeuristicLab.Optimization/3.3/MetaOptimizers/Experiment.cs	(revision 15573)
@@ -97,4 +97,14 @@
     }
 
+    [Storable]
+    private int numberOfWorkers = 1;
+    public int NumberOfWorkers {
+      get { return numberOfWorkers; }
+      set {
+        if (value < 1) throw new ArgumentException("Number of Workers must not be lower than one.");
+        numberOfWorkers = value;
+      }
+    }
+
     public IEnumerable<IOptimizer> NestedOptimizers {
       get {
@@ -111,5 +121,15 @@
     private bool experimentStarted = false;
     private bool experimentStopped = false;
-    private readonly ManualResetEventSlim allOptimizerFinished = new ManualResetEventSlim(false); // this indicates that all started optimizers have been paused or stopped
+
+    // track already started optimizers (.StartAsync does not set the executionstate immediately)
+    // and to avoid restarting optimizers that were manually paused/stopped by the user
+    private readonly IDictionary<IOptimizer, Task> startedOptimizers = new Dictionary<IOptimizer, Task>();
+    private IEnumerable<IOptimizer> StartableOptimizers {
+      get {
+        return Optimizers
+          .Where(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused)
+          .Where(o => !startedOptimizers.ContainsKey(o));  // all startable optimizers that were not startet yet
+      }
+    }
 
     public Experiment()
@@ -155,4 +175,5 @@
       experimentStarted = original.experimentStarted;
       experimentStopped = original.experimentStopped;
+      numberOfWorkers = original.numberOfWorkers;
       Initialize();
     }
@@ -192,16 +213,28 @@
       if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
         throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
-      if (!Optimizers.Any(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused)) return;
+
+      startedOptimizers.Clear();
+      if (!StartableOptimizers.Any()) return;
 
       experimentStarted = true;
       experimentStopped = false;
-      allOptimizerFinished.Reset();
-      IOptimizer optimizer;
-      while ((optimizer = Optimizers.FirstOrDefault(x => x.ExecutionState == ExecutionState.Prepared || x.ExecutionState == ExecutionState.Paused)) != null) {
-        // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { optimizer.Start(cancellationToken); } catch (InvalidOperationException) { }
-        if (ExecutionState == ExecutionState.Paused || ExecutionState == ExecutionState.Stopped) break;
-      }
-      allOptimizerFinished.Wait();
+
+      using (var availableWorkers = new SemaphoreSlim(NumberOfWorkers, NumberOfWorkers)) {
+        while (StartableOptimizers.Any()) {
+          try {
+            availableWorkers.Wait(cancellationToken);
+            var optimizer = StartableOptimizers.FirstOrDefault();
+            if (experimentStopped || !experimentStarted || optimizer == null) break;
+
+            var startedTask = optimizer.StartAsync(cancellationToken).ContinueWith(async t => {
+              availableWorkers.Release(); // is guaranteed to be not disposed yet because Task.WaitAll blocks before the end of the using
+              await t; // trigger a potential exception on the optimizerTask
+            });
+            startedOptimizers.Add(optimizer, startedTask.Unwrap()); // unwrap task because lambda of .ContinueWith is async
+          } catch (InvalidOperationException) { } catch (OperationCanceledException) { }
+        }
+
+        Task.WaitAll(startedOptimizers.Values.ToArray()); // retrieve exeptions of the asyncrounously started optimizer
+      }
     }
     public async Task StartAsync() { await StartAsync(CancellationToken.None); }
@@ -218,5 +251,5 @@
       foreach (IOptimizer optimizer in Optimizers.Where(x => x.ExecutionState == ExecutionState.Started)) {
         // a race-condition may occur when the optimizer has changed the state by itself in the meantime
-        try { optimizer.Pause(); } catch (InvalidOperationException) { }
+        try { optimizer.Pause(); } catch (InvalidOperationException) { } catch (NotSupportedException) { }
       }
     }
@@ -256,4 +289,5 @@
     public event EventHandler Prepared;
     private void OnPrepared() {
+      if (ExecutionState == ExecutionState.Prepared) return;
       ExecutionState = ExecutionState.Prepared;
       EventHandler handler = Prepared;
@@ -262,4 +296,5 @@
     public event EventHandler Started;
     private void OnStarted() {
+      if (ExecutionState == ExecutionState.Started) return;
       ExecutionState = ExecutionState.Started;
       EventHandler handler = Started;
@@ -268,6 +303,6 @@
     public event EventHandler Paused;
     private void OnPaused() {
+      if (ExecutionState == ExecutionState.Paused) return;
       ExecutionState = ExecutionState.Paused;
-      allOptimizerFinished.Set();
       EventHandler handler = Paused;
       if (handler != null) handler(this, EventArgs.Empty);
@@ -275,6 +310,6 @@
     public event EventHandler Stopped;
     private void OnStopped() {
+      if (ExecutionState == ExecutionState.Stopped) return;
       ExecutionState = ExecutionState.Stopped;
-      allOptimizerFinished.Set();
       EventHandler handler = Stopped;
       if (handler != null) handler(this, EventArgs.Empty);
@@ -372,10 +407,8 @@
     }
     private void optimizer_Paused(object sender, EventArgs e) {
-      lock (locker)
-        if (Optimizers.All(x => x.ExecutionState != ExecutionState.Started)) OnPaused();
+      UpdateExecutionState();
     }
     private void optimizer_Prepared(object sender, EventArgs e) {
-      lock (locker)
-        if (Optimizers.All(x => x.ExecutionState == ExecutionState.Prepared)) OnPrepared();
+      UpdateExecutionState();
     }
     private void optimizer_Started(object sender, EventArgs e) {
@@ -384,14 +417,44 @@
     }
     private void optimizer_Stopped(object sender, EventArgs e) {
+      UpdateExecutionState();
+    }
+    private void UpdateExecutionState() {
+      // Execution states of the Experiment are determined using the following _basic_ rules:
+      //   if any Optimizer is Started      => Experiment is Started  (2. if)
+      //   if any Optimizer is Paused       => Experiment is Paused   (3. if)
+      //   if any Optimizer is Prepared     => Experiment is Prepared (5. if)
+      //   else (all Optimizer are Stopped) => Experiment is Stopped  (6. if)
+      // Additional there are two extra rules:
+      //   if the Experiment is running and there are still optimizers that can be started => keep the Experiment Running (1. if)
+      //   if experiment-stop is pending: Stop Experiment even if there are still Prepared Optimizer               (4. if)
+
       lock (locker) {
-        if (experimentStopped) {
-          if (Optimizers.All(x => (x.ExecutionState == ExecutionState.Stopped) || (x.ExecutionState == ExecutionState.Prepared))) OnStopped();
-        } else {
-          if (experimentStarted && Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused))) return;
-          else if (Optimizers.All(x => x.ExecutionState == ExecutionState.Stopped)) OnStopped();
-          else if (Optimizers.Any(x => (x.ExecutionState == ExecutionState.Prepared) || (x.ExecutionState == ExecutionState.Paused)) && Optimizers.All(o => o.ExecutionState != ExecutionState.Started)) OnPaused();
-        }
-      }
-    }
+        // 1. experiment is running & further startable optimizers are available => continue executing
+        if (experimentStarted && StartableOptimizers.Any())
+          return;
+
+        // 2. any optimizer is running => continue executing
+        if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Started))
+          return;
+
+        experimentStarted = false;
+        // 3. any optimizer is paused => experiment paused
+        if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Paused))
+          OnPaused();
+
+        // 4. stop pending & all optimizers either stopped or prepared => experiment stopped
+        else if (experimentStopped)
+          OnStopped();
+
+        // 5. any optimizer prepared => experiment prepared
+        else if (Optimizers.Any(x => x.ExecutionState == ExecutionState.Prepared))
+          OnPrepared();
+
+        // 6. (else) all optimizers stopped
+        else
+          OnStopped();
+      }
+    }
+
     private void optimizer_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
       lock (runsLocker) {
