Repository: microsoft/Qcodes

Issue:
Data mingled across columns in dataset
### Steps to reproduce
1. Measure something with `.run(use_threads=True)`

### Expected behaviour
Get a neat dataset

### Actual behaviour
Data mingles across columns in dataset 💘 

### System
**operating system**
Win 10

**qcodes branch**
Master

Two identical measurements:
![image](https://cloud.githubusercontent.com/assets/8595386/26193146/99b0d318-3bb4-11e7-9e93-473d782fe41b.png)




Discussion:
just an example data from a different dataset, see the lines marked with love, columns 2 and 3 switch around...

```
-5.994    4.27718    0.0173826    0.0467188
-5.991    4.27718    0.0173826    0.0467227
-5.988    0.0173826    4.27718    0.0467227 # <3
-5.985    4.27718    0.0173826    0.0467227
-5.982    4.27718    0.0173826    0.0467227
-5.979    4.27718    0.0173826    0.0467188
-5.976    4.27718    0.0173826    0.0467227
-5.973    4.27718    0.0173826    0.0467227
-5.97    0.0173826    4.27718    0.0467227 # <3
-5.967    4.27718    0.0173826    0.0467227
-5.964    0.0173826    4.27718    0.0467227 # <3
-5.961    4.27718    0.0173826    0.0467265
-5.958    4.27718    0.0173826    0.0467265
-5.955    4.27718    0.0173826    0.0467265
-5.952    4.27718    0.0192095    0.0467303
-5.949    4.27718    0.0192095    0.0467265
-5.946    4.27718    0.0192095    0.0467265
-5.943    4.27718    0.0192095    0.0467265
-5.94    0.0192095    4.27718    0.0467265 # <3
-5.937    4.27718    0.0192095    0.0467303
-5.934    4.27718    0.0192095    0.0467303
-5.931    4.27718    0.0192095    0.0467303
```
Thats really bad. I tried reproducing it with a couple of dummy instruments but didn't see it. Are you measuring the 3 parameters from the same instrument? I am wondering if it's the driver which is not thread safe? 
@jenshnielsen  I think that's def a possibility. 
yup, just commented to @giulioungaretti on slack, it happens when I use two parameters on the same instrument, in this case its an SR830 lock-in
and works fine with only on parameter per instrument


Resolving patch:
diff --git a/qcodes/actions.py b/qcodes/actions.py
--- a/qcodes/actions.py
+++ b/qcodes/actions.py
@@ -8,6 +8,12 @@
 _NO_SNAPSHOT = {'type': None, 'description': 'Action without snapshot'}
 
 
+# exception when threading is attempted used to simultaneously
+# query the same instrument for several values
+class UnsafeThreadingException(Exception):
+    pass
+
+
 def _actions_snapshot(actions, update):
     """Make a list of snapshots from a list of actions."""
     snapshot = []
@@ -113,9 +119,13 @@ def __init__(self, params_indices, data_set, use_threads):
         self.getters = []
         self.param_ids = []
         self.composite = []
+        paramcheck = []  # list to check if parameters are unique
         for param, action_indices in params_indices:
             self.getters.append(param.get)
 
+            if param._instrument:
+                paramcheck.append((param, param._instrument))
+
             if hasattr(param, 'names'):
                 part_ids = []
                 for i in range(len(param.names)):
@@ -128,6 +138,17 @@ def __init__(self, params_indices, data_set, use_threads):
                 self.param_ids.append(param_id)
                 self.composite.append(False)
 
+        if self.use_threads:
+            insts = [p[1] for p in paramcheck]
+            if (len(set(insts)) != len(insts)):
+                duplicates = [p for p in paramcheck if insts.count(p[1]) > 1]
+                raise UnsafeThreadingException('Can not use threading to '
+                                               'read '
+                                               'several things from the same '
+                                               'instrument. Specifically, you '
+                                               'asked for'
+                                               ' {}.'.format(duplicates))
+
     def __call__(self, loop_indices, **ignore_kwargs):
         out_dict = {}
         if self.use_threads:
@@ -166,7 +187,7 @@ class BreakIf:
 
     """
     Loop action that breaks out of the loop if a condition is truthy.
-    
+
     Args:
         condition (callable): a callable taking no arguments.
             Can be a simple function that returns truthy when it's time to quit


Test patch:
diff --git a/qcodes/tests/test_threading.py b/qcodes/tests/test_threading.py
new file mode 100644
--- /dev/null
+++ b/qcodes/tests/test_threading.py
@@ -0,0 +1,32 @@
+import gc
+
+from unittest import TestCase
+
+from qcodes import Loop
+from qcodes.actions import UnsafeThreadingException
+from qcodes.tests.instrument_mocks import DummyInstrument
+
+
+class TestUnsafeThreading(TestCase):
+
+    def setUp(self):
+        self.inst1 = DummyInstrument(name='inst1',
+                                     gates=['v1', 'v2'])
+        self.inst2 = DummyInstrument(name='inst2',
+                                     gates=['v1', 'v2'])
+
+    def tearDown(self):
+        self.inst1.close()
+        self.inst2.close()
+
+        del self.inst1
+        del self.inst2
+
+        gc.collect()
+
+    def test_unsafe_exception(self):
+        to_meas = (self.inst1.v1, self.inst1.v2)
+        loop = Loop(self.inst2.v1.sweep(0, 1, num=10)).each(*to_meas)
+
+        with self.assertRaises(UnsafeThreadingException):
+            loop.run(use_threads=True)