Resource Locking
How to use PyVISA resource locking to safely share instruments across multiple scripts and processes.
When two scripts talk to the same instrument at the same time, commands interleave and results corrupt. PyVISA's locking mechanism prevents this with exclusive and shared locks.
Exclusive Lock
An exclusive lock gives one session full control. Other sessions block on any read/write until you release it.
import pyvisa
rm = pyvisa.ResourceManager()
dmm = rm.open_resource("TCPIP0::192.168.1.50::inst0::INSTR")
dmm.timeout = 10000
try:
dmm.lock_excl(timeout=5000)
dmm.write("*RST")
dmm.write("CONF:VOLT:DC 10, 0.001")
voltage = float(dmm.query("READ?"))
print(f"DC Voltage: {voltage:.6f} V")
dmm.unlock()
finally:
dmm.close()
rm.close()Log measurements automatically
TofuPilot records test results from your PyVISA scripts, tracks pass/fail rates across stations sharing the same instruments. Free to start.
Use lock_context to Avoid Forgetting unlock
If your code throws an exception between lock() and unlock(), the instrument stays locked until the session closes. Use lock_context() to handle this automatically.
import pyvisa
rm = pyvisa.ResourceManager()
dmm = rm.open_resource("TCPIP0::192.168.1.50::inst0::INSTR")
dmm.timeout = 10000
try:
with dmm.lock_context(timeout=5000):
dmm.write("CONF:VOLT:DC")
voltage = float(dmm.query("READ?"))
print(f"Voltage: {voltage:.6f} V")
dmm.write("CONF:RES")
resistance = float(dmm.query("READ?"))
print(f"Resistance: {resistance:.2f} ohm")
# Lock released here, even if an exception was raised
finally:
dmm.close()
rm.close()Always use lock_context
Calling lock_excl() and unlock() manually is error-prone. If anything raises between the two calls, the lock holds until session close. Prefer lock_context().
Shared Lock
A shared lock allows multiple sessions to read from an instrument simultaneously, but blocks exclusive lock requests. Useful for monitoring scripts that only query status.
import pyvisa
rm = pyvisa.ResourceManager()
dmm = rm.open_resource("TCPIP0::192.168.1.50::inst0::INSTR")
try:
with dmm.lock_context(timeout=5000, requested_key="monitor_group"):
# Multiple scripts using the same key can hold this lock
idn = dmm.query("*IDN?")
stb = dmm.read_stb()
print(f"Instrument: {idn.strip()}")
print(f"Status byte: {stb:#04x}")
finally:
dmm.close()
rm.close()When you pass a requested_key, PyVISA requests a shared lock. All sessions using the same key can access the instrument concurrently. An exclusive lock request will block until all shared locks release.
Two Scripts Sharing a Network DMM
Script A runs a measurement sequence with an exclusive lock. Script B monitors the DMM status with a shared lock. They never step on each other.
Script A (measurement):
import pyvisa
import time
rm = pyvisa.ResourceManager()
dmm = rm.open_resource("TCPIP0::192.168.1.50::inst0::INSTR")
dmm.timeout = 10000
try:
with dmm.lock_context(timeout=10000):
dmm.write("*RST")
dmm.write("CONF:VOLT:DC 10, 0.0001")
dmm.write("SAMP:COUN 50")
dmm.write("TRIG:SOUR IMM")
dmm.write("INIT")
time.sleep(2)
data = dmm.query("FETC?")
values = [float(v) for v in data.strip().split(",")]
print(f"Measured {len(values)} samples, mean: {sum(values)/len(values):.6f} V")
finally:
dmm.close()
rm.close()Script B (monitor):
import pyvisa
rm = pyvisa.ResourceManager()
dmm = rm.open_resource("TCPIP0::192.168.1.50::inst0::INSTR")
try:
with dmm.lock_context(timeout=5000, requested_key="status_monitor"):
idn = dmm.query("*IDN?")
err = dmm.query("SYST:ERR?")
print(f"Instrument: {idn.strip()}")
print(f"Error queue: {err.strip()}")
finally:
dmm.close()
rm.close()Lock Types
| Lock type | Method | Blocks others? | Multiple holders? | Use case |
|---|---|---|---|---|
| Exclusive | lock_excl() or lock_context() | Yes, all access blocked | No, single holder | Measurement sequences, configuration |
| Shared | lock_context(requested_key="...") | Blocks exclusive only | Yes, same key | Status monitoring, read-only queries |
Timeout Behavior
| Scenario | What happens |
|---|---|
| Lock available | Acquired immediately, returns |
| Lock held, timeout > 0 | Blocks up to timeout (ms), raises VisaIOError on expiry |
| Lock held, timeout = 0 | Fails immediately with VisaIOError |
Lock held, timeout = VI_TMO_INFINITE | Blocks indefinitely until lock is released |
Common Pitfalls
| Problem | Fix |
|---|---|
| Lock not released on exception | Use lock_context() instead of manual lock_excl()/unlock() |
| Script hangs waiting for lock | Set a finite timeout, check if another script crashed while holding the lock |
| Shared lock blocks exclusive | All shared lock holders must release before exclusive can acquire |
| Lock works locally but not over network | Both scripts must connect to the same VISA resource string |