diff --git a/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h b/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h
index 1827259f0d7232f40c5dab8a01346916fdb7a133..4b8ebfaa3b7c018a25a1d618fc941c1a602884db 100644
--- a/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h
+++ b/Framework/Algorithms/inc/MantidAlgorithms/SpectrumAlgorithm.h
@@ -88,7 +88,7 @@ private:
     auto size = static_cast<int64_t>(indexSet.size());
     API::Progress progress(this, 0.0, 1.0, size);
 
-    PARALLEL_FOR1((&workspace))
+    PARALLEL_FOR_IF(Kernel::threadSafe(workspace))
     for (int64_t i = 0; i < size; ++i) {
       PARALLEL_START_INTERUPT_REGION
       // Note the small but for now negligible overhead from the IndexSet access
diff --git a/Framework/Algorithms/src/AlignDetectors.cpp b/Framework/Algorithms/src/AlignDetectors.cpp
index 8d553002cf40c209660d5fae826ec1bad5f1df4b..799abab078eb4b6d4b3a9255ebfbb503059294b3 100644
--- a/Framework/Algorithms/src/AlignDetectors.cpp
+++ b/Framework/Algorithms/src/AlignDetectors.cpp
@@ -326,7 +326,7 @@ void AlignDetectors::exec() {
 
 void AlignDetectors::align(const ConversionFactors &converter,
                            Progress &progress, MatrixWorkspace &outputWS) {
-  PARALLEL_FOR1((&outputWS))
+  PARALLEL_FOR_IF(Kernel::threadSafe(outputWS))
   for (int64_t i = 0; i < m_numberOfSpectra; ++i) {
     PARALLEL_START_INTERUPT_REGION
     try {
diff --git a/Framework/Algorithms/src/BinaryOperation.cpp b/Framework/Algorithms/src/BinaryOperation.cpp
index f4d7e154399e4dad36eee09d8a627728440420a6..2408a18fd52dd668977a88e0344204d54f3ead14 100644
--- a/Framework/Algorithms/src/BinaryOperation.cpp
+++ b/Framework/Algorithms/src/BinaryOperation.cpp
@@ -492,7 +492,7 @@ void BinaryOperation::doSingleValue() {
 
   if (m_eout) {
     // ---- The output is an EventWorkspace ------
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       m_out->setX(i, m_lhs->refX(i));
@@ -503,7 +503,7 @@ void BinaryOperation::doSingleValue() {
     PARALLEL_CHECK_INTERUPT_REGION
   } else {
     // ---- Histogram Output -----
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       m_out->setX(i, m_lhs->refX(i));
@@ -537,7 +537,7 @@ void BinaryOperation::doSingleColumn() {
   const int64_t numHists = m_lhs->getNumberHistograms();
   if (m_eout) {
     // ---- The output is an EventWorkspace ------
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       const double rhsY = m_rhs->readY(i)[0];
@@ -553,7 +553,7 @@ void BinaryOperation::doSingleColumn() {
     PARALLEL_CHECK_INTERUPT_REGION
   } else {
     // ---- Histogram Output -----
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       const double rhsY = m_rhs->readY(i)[0];
@@ -599,7 +599,7 @@ void BinaryOperation::doSingleSpectrum() {
       // Now loop over the spectra of the left hand side calling the virtual
       // function
       const int64_t numHists = m_lhs->getNumberHistograms();
-      PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+      PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
       for (int64_t i = 0; i < numHists; ++i) {
         PARALLEL_START_INTERUPT_REGION
         // m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied
@@ -622,7 +622,7 @@ void BinaryOperation::doSingleSpectrum() {
       // function
       const int64_t numHists = m_lhs->getNumberHistograms();
 
-      PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+      PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
       for (int64_t i = 0; i < numHists; ++i) {
         PARALLEL_START_INTERUPT_REGION
         // m_out->setX(i,m_lhs->refX(i)); //unnecessary - that was copied
@@ -648,7 +648,7 @@ void BinaryOperation::doSingleSpectrum() {
     // function
     const int64_t numHists = m_lhs->getNumberHistograms();
 
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       m_out->setX(i, m_lhs->refX(i));
@@ -693,7 +693,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) {
       // ------------ The rhs is ALSO an EventWorkspace ---------------
       // Now loop over the spectra of each one calling the virtual function
       const int64_t numHists = m_lhs->getNumberHistograms();
-      PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+      PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
       for (int64_t i = 0; i < numHists; ++i) {
         PARALLEL_START_INTERUPT_REGION
         m_progress->report(this->name());
@@ -726,7 +726,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) {
       // Now loop over the spectra of each one calling the virtual function
       const int64_t numHists = m_lhs->getNumberHistograms();
 
-      PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+      PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
       for (int64_t i = 0; i < numHists; ++i) {
         PARALLEL_START_INTERUPT_REGION
         m_progress->report(this->name());
@@ -763,7 +763,7 @@ void BinaryOperation::do2D(bool mismatchedSpectra) {
     // Now loop over the spectra of each one calling the virtual function
     const int64_t numHists = m_lhs->getNumberHistograms();
 
-    PARALLEL_FOR3(m_lhs, m_rhs, m_out)
+    PARALLEL_FOR_IF(Kernel::threadSafe(*m_lhs, *m_rhs, *m_out))
     for (int64_t i = 0; i < numHists; ++i) {
       PARALLEL_START_INTERUPT_REGION
       m_progress->report(this->name());
diff --git a/Framework/Algorithms/src/DetectorDiagnostic.cpp b/Framework/Algorithms/src/DetectorDiagnostic.cpp
index 4c57f308791efde2811296ec9b4cd7d7de9a0791..dbb315c6bfb33b7ac14cbbbe97676f9e383caabb 100644
--- a/Framework/Algorithms/src/DetectorDiagnostic.cpp
+++ b/Framework/Algorithms/src/DetectorDiagnostic.cpp
@@ -621,7 +621,7 @@ std::vector<double> DetectorDiagnostic::calculateMedian(
                       (instrument->getSample() != nullptr));
     }
 
-    PARALLEL_FOR1((&input))
+    PARALLEL_FOR_IF(Kernel::threadSafe(input))
     for (int i = 0; i < nhists; ++i) { // NOLINT
       PARALLEL_START_INTERUPT_REGION
 
diff --git a/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp b/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp
index 101c657e0bb916de257aa2096c09fa5d80ca9192..dc2214496a3546402fbab56d3d8aedfe8500b80b 100644
--- a/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp
+++ b/Framework/Algorithms/src/DetectorEfficiencyVariation.cpp
@@ -210,7 +210,7 @@ int DetectorEfficiencyVariation::doDetectorTests(
   const double deadValue(1.0);
   int numFailed(0);
   const auto &spectrumInfo = counts1->spectrumInfo();
-  PARALLEL_FOR3(counts1, counts2, maskWS)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*counts1, *counts2, *maskWS))
   for (int i = 0; i < numSpec; ++i) {
     PARALLEL_START_INTERUPT_REGION
     // move progress bar
diff --git a/Framework/Algorithms/src/NormaliseToMonitor.cpp b/Framework/Algorithms/src/NormaliseToMonitor.cpp
index 1ab370ee0d21fcd82ab2fd8bb0227da69731f29c..fe915ac54f877febba39c1b36ae91f5d49d241a3 100644
--- a/Framework/Algorithms/src/NormaliseToMonitor.cpp
+++ b/Framework/Algorithms/src/NormaliseToMonitor.cpp
@@ -574,7 +574,8 @@ void NormaliseToMonitor::normaliseBinByBin(
   bool hasZeroDivision = false;
   Progress prog(this, 0.0, 1.0, numHists);
   // Loop over spectra
-  PARALLEL_FOR3(inputWorkspace, outputWorkspace, m_monitor)
+  PARALLEL_FOR_IF(
+      Kernel::threadSafe(*inputWorkspace, *outputWorkspace, *m_monitor))
   for (int64_t i = 0; i < int64_t(numHists); ++i) {
     PARALLEL_START_INTERUPT_REGION
     prog.report();
diff --git a/Framework/Algorithms/src/PointByPointVCorrection.cpp b/Framework/Algorithms/src/PointByPointVCorrection.cpp
index 2b2de14b3b68b74c5c506643b2a8cae89b21d240..ef1cf8094db94013ae874270bf74e544054b27f8 100644
--- a/Framework/Algorithms/src/PointByPointVCorrection.cpp
+++ b/Framework/Algorithms/src/PointByPointVCorrection.cpp
@@ -53,7 +53,7 @@ void PointByPointVCorrection::exec() {
   const int nHist = static_cast<int>(inputWS1->getNumberHistograms());
   Progress prog(this, 0.0, 1.0, nHist);
 
-  PARALLEL_FOR3(inputWS1, inputWS2, outputWS)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS1, *inputWS2, *outputWS))
   for (int i = 0; i < nHist; i++) // Looping on all histograms
   {
     PARALLEL_START_INTERUPT_REGION
diff --git a/Framework/Algorithms/src/Q1D2.cpp b/Framework/Algorithms/src/Q1D2.cpp
index 5c97a099e5364eb60e32dcfd19ce684fcb2399b7..fb15129a72c62bad93e6ca50850d61bb13d9261a 100644
--- a/Framework/Algorithms/src/Q1D2.cpp
+++ b/Framework/Algorithms/src/Q1D2.cpp
@@ -146,7 +146,7 @@ void Q1D2::exec() {
   Progress progress(this, 0.05, 1.0, numSpec + 1);
 
   const auto &spectrumInfo = m_dataWS->spectrumInfo();
-  PARALLEL_FOR3(m_dataWS, outputWS, pixelAdj)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*m_dataWS, *outputWS, pixelAdj.get()))
   for (int i = 0; i < numSpec; ++i) {
     PARALLEL_START_INTERUPT_REGION
     if (!spectrumInfo.hasDetectors(i)) {
diff --git a/Framework/Algorithms/src/Rebin.cpp b/Framework/Algorithms/src/Rebin.cpp
index 474a29507493d61ff950fcc179a1950fc7b859f6..64388219cf672609ce49b1fa75e91050dc9d1845 100644
--- a/Framework/Algorithms/src/Rebin.cpp
+++ b/Framework/Algorithms/src/Rebin.cpp
@@ -179,7 +179,7 @@ void Rebin::exec() {
       Progress prog(this, 0.0, 1.0, histnumber);
 
       // Go through all the histograms and set the data
-      PARALLEL_FOR3(inputWS, eventInputWS, outputWS)
+      PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *outputWS))
       for (int i = 0; i < histnumber; ++i) {
         PARALLEL_START_INTERUPT_REGION
 
diff --git a/Framework/Crystal/src/MaskPeaksWorkspace.cpp b/Framework/Crystal/src/MaskPeaksWorkspace.cpp
index 3ba6cbe3eae5e0aec19f4e4bc5a6892989812b80..378be6b7113f2edc1b53afc1d796baaf688afb2d 100644
--- a/Framework/Crystal/src/MaskPeaksWorkspace.cpp
+++ b/Framework/Crystal/src/MaskPeaksWorkspace.cpp
@@ -89,7 +89,7 @@ void MaskPeaksWorkspace::exec() {
 
   // Loop over peaks
   const std::vector<Peak> &peaks = peaksW->getPeaks();
-  PARALLEL_FOR3(m_inputW, peaksW, tablews)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*m_inputW, *peaksW, *tablews))
   for (int i = 0; i < static_cast<int>(peaks.size()); i++) { // NOLINT
     PARALLEL_START_INTERUPT_REGION
     const Peak &peak = peaks[i];
diff --git a/Framework/Crystal/src/PeakIntegration.cpp b/Framework/Crystal/src/PeakIntegration.cpp
index 218265137bb6def382e09a7249481ec1e2253857..c1524ad285c6567e5695450e65bda98c8f757349 100644
--- a/Framework/Crystal/src/PeakIntegration.cpp
+++ b/Framework/Crystal/src/PeakIntegration.cpp
@@ -124,7 +124,7 @@ void PeakIntegration::exec() {
   }
 
   Progress prog(this, MinPeaks, 1.0, NumberPeaks);
-  PARALLEL_FOR3(inputW, peaksW, outputW)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*inputW, *peaksW, *outputW))
   for (int i = MinPeaks; i < NumberPeaks; i++) {
 
     PARALLEL_START_INTERUPT_REGION
diff --git a/Framework/Crystal/src/SCDCalibratePanels.cpp b/Framework/Crystal/src/SCDCalibratePanels.cpp
index dbfefc0d28ed1e3c0865c15330c93d5a699d7841..dd4f20a3a277ee2c58a1d9a2178d013ca2bd80ad 100644
--- a/Framework/Crystal/src/SCDCalibratePanels.cpp
+++ b/Framework/Crystal/src/SCDCalibratePanels.cpp
@@ -371,7 +371,7 @@ void SCDCalibratePanels::exec() {
   DblMatrix UB = lattice.getUB();
   // sort again since edge peaks can trace to other banks
   peaksWs->sort(criteria);
-  PARALLEL_FOR3(ColWksp, RowWksp, TofWksp)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*ColWksp, *RowWksp, *TofWksp))
   for (int i = 0; i < static_cast<int>(MyBankNames.size()); ++i) {
     PARALLEL_START_INTERUPT_REGION
     const std::string &bankName = *std::next(MyBankNames.begin(), i);
diff --git a/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp b/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp
index bceffdf53e839c586f11f7086b84124da8fc63e7..cdf6947d8a7cdf8e71abb73bfe6b06ee45527ad5 100644
--- a/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp
+++ b/Framework/CurveFitting/src/Algorithms/ConvolveWorkspaces.cpp
@@ -62,7 +62,7 @@ void ConvolveWorkspaces::exec() {
 
   prog = new Progress(this, 0.0, 1.0, numHists);
   // Now convolve the histograms
-  PARALLEL_FOR3(ws1, ws2, outputWS)
+  PARALLEL_FOR_IF(Kernel::threadSafe(*ws1, *ws2, *outputWS))
   for (int l = 0; l < static_cast<int>(numHists); ++l) {
     PARALLEL_START_INTERUPT_REGION
     prog->report();
diff --git a/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp b/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp
index cb3db378fc77bdb1c629190cf6c15ff2ffbed78e..dcbb982f1c7c744a57c4afd58e087ef8303ed3b3 100644
--- a/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp
+++ b/Framework/CurveFitting/src/Algorithms/VesuvioCalculateGammaBackground.cpp
@@ -115,7 +115,8 @@ void VesuvioCalculateGammaBackground::exec() {
       10 + nhist * (m_npeaks + 2 * m_foils0.size() * NTHETA * NUP * m_npeaks);
   m_progress = new Progress(this, 0.0, 1.0, nreports);
 
-  PARALLEL_FOR3(m_inputWS, m_correctedWS, m_backgroundWS)
+  PARALLEL_FOR_IF(
+      Kernel::threadSafe(*m_inputWS, *m_correctedWS, *m_backgroundWS))
   for (int64_t i = 0; i < nhist; ++i) {
     PARALLEL_START_INTERUPT_REGION
     const size_t outputIndex = i;
diff --git a/Framework/Kernel/inc/MantidKernel/MultiThreaded.h b/Framework/Kernel/inc/MantidKernel/MultiThreaded.h
index c7b8bc90f5a152bc4b9fca56b3122e3ae7351f65..07014cbba03a54ca45dede942876485463bc8b8b 100644
--- a/Framework/Kernel/inc/MantidKernel/MultiThreaded.h
+++ b/Framework/Kernel/inc/MantidKernel/MultiThreaded.h
@@ -1,11 +1,75 @@
 #ifndef MANTID_KERNEL_MULTITHREADED_H_
 #define MANTID_KERNEL_MULTITHREADED_H_
 
+#include "MantidKernel/DataItem.h"
+
 #include <mutex>
 
 namespace Mantid {
-namespace Kernel {} // namespace
-} // namespace
+namespace Kernel {
+
+/** Thread-safety check
+ * Checks the workspace to ensure it is suitable for multithreaded access.
+ * NULL workspaces are assumed suitable
+ * @param workspace pointer to workspace to verify.
+ * @return Whether workspace is threadsafe.
+ */
+template <typename Arg>
+inline typename std::enable_if<std::is_pointer<Arg>::value, bool>::type
+threadSafe(Arg workspace) {
+  static_assert(
+      std::is_base_of<DataItem, typename std::remove_pointer<Arg>::type>::value,
+      "Parameter must be derived from Mantid::Kernel::DataItem!");
+  return !workspace || workspace->threadSafe();
+}
+
+/** Thread-safety check
+  * Checks the workspace to ensure it is suitable for multithreaded access.
+  * NULL workspaces are assumed suitable
+  * @param workspace pointer to workspace to verify.
+  * @param others pointers to all other workspaces which need to be checked.
+  * @return whether workspace is threadsafe.
+  */
+template <typename Arg, typename... Args>
+inline typename std::enable_if<std::is_pointer<Arg>::value, bool>::type
+threadSafe(Arg workspace, Args &&... others) {
+  static_assert(
+      std::is_base_of<DataItem, typename std::remove_pointer<Arg>::type>::value,
+      "Parameter must be derived from Mantid::Kernel::DataItem!");
+  return (!workspace || workspace->threadSafe()) &&
+         threadSafe(std::forward<Args>(others)...);
+}
+
+/** Thread-safety check
+ * Checks the workspace to ensure it is suitable for multithreaded access.
+ * @param workspace reference to workspace to verify.
+ * @return Whether workspace is threadsafe.
+ */
+template <typename Arg>
+inline typename std::enable_if<!std::is_pointer<Arg>::value, bool>::type
+threadSafe(const Arg &workspace) {
+  static_assert(std::is_base_of<DataItem, Arg>::value,
+                "Parameter must be derived from Mantid::Kernel::DataItem!");
+  return workspace.threadSafe();
+}
+
+/** Thread-safety check
+ * Checks the workspace to ensure it is suitable for multithreaded access.
+ * @param workspace reference to workspace to verify.
+ * @param others references or pointers to all other workspaces which need to be
+ * checked.
+ * @return whether workspace is threadsafe.
+ */
+template <typename Arg, typename... Args>
+inline typename std::enable_if<!std::is_pointer<Arg>::value, bool>::type
+threadSafe(const Arg &workspace, Args &&... others) {
+  static_assert(std::is_base_of<DataItem, Arg>::value,
+                "Parameter must be derived from Mantid::Kernel::DataItem!");
+  return workspace.threadSafe() && threadSafe(std::forward<Args>(others)...);
+}
+
+} // namespace Kernel
+} // namespace Mantid
 
 // The syntax used to define a pragma within a macro is different on windows and
 // GCC
@@ -98,16 +162,6 @@ namespace Kernel {} // namespace
     PRAGMA(omp parallel for if ( ( !workspace1 || workspace1->threadSafe() ) && \
     ( !workspace2 || workspace2->threadSafe() ) ))
 
-/** Includes code to add OpenMP commands to run the next for loop in parallel.
-*	 All three workspaces are checked to ensure they are suitable for
-*multithreaded access
-*  but NULL workspaces are assumed to be safe
-*/
-#define PARALLEL_FOR3(workspace1, workspace2, workspace3)                      \
-    PRAGMA(omp parallel for if ( (!workspace1 || workspace1->threadSafe()) && \
-    ( !workspace2 || workspace2->threadSafe() ) && \
-    ( !workspace3 || workspace3->threadSafe() ) ))
-
 /** Ensures that the next execution line or block is only executed if
 * there are multple threads execting in this region
 */
@@ -164,7 +218,6 @@ namespace Kernel {} // namespace
 #define PARALLEL_FOR_NO_WSP_CHECK_FIRSTPRIVATE2(variable1, variable2)
 #define PARALLEL_FOR1(workspace1)
 #define PARALLEL_FOR2(workspace1, workspace2)
-#define PARALLEL_FOR3(workspace1, workspace2, workspace3)
 #define IF_PARALLEL if (false)
 #define IF_NOT_PARALLEL
 #define PARALLEL_CRITICAL(name)