Using Multithreading - SwiftVISA/SwiftVISA GitHub Wiki
Using Multithreading
By default any methods on instruments in SwiftVISA run on the main thread. This is fine for command line applications and for simple projects where GUI responsiveness is not important. Whenever a method is called that requires communication with an instrument, the thread will freeze until the communication has completed. Because communication usually takes a fair amount of time, this will lead to you application appearing to be frozen during these operations. Further, when running code on the main thread, only one instrument can be communicating at any given time. In many situations it is important to keep the GUI as responsive as possible, and in some situations it is preferable to be able to communicate with multiple instruments at the same time. In these situations you should consider multithreading.
##Note: ## This guide assumes that you have a working knowledge of Dispatch and multithreading principles.
SwiftVISA has full multithreading support. Any method on instruments can be executed on any thread. Further, each instrument has a dispatchQueue
property that returns a unique thread for you to use to communicate with the instrument. This property is lazy, meaning that the dispatch queue is not created until you first access it. This prevents excess threads from being created by your application. Below is an example of using the dispatchQueue
property to execute code on another thread:
let instrument: Instrument = // Make instrument here
instrument.dispatchQueue.async {
// Run something on another thread
}
While the dispatchQueue
property is useful in most situations there are certain scenarios where you may need to execute operations on a different thread than the one provided by the dispatchQueue
property. For example, you may need to wait until a certain operation completes on an instrument before executing an operation on another instrument. A naïve solution would be the code below:
let instrument1: Instrument = // Make instrument here
let instrument2: Instrument = // Make instrument here
let something: Any!
instrument1.dispatchQueue.async {
something = // Get value from instrument1
}
instrument2.dispatchQueue.async {
instrument2.doSomething(something)
}
However this code will not work. Because something is initialized on instrument1
's thread, it may not be available by the time that instrument2
's thread will try to access it. Another naïve solution would be the following:
let instrument1: Instrument = // Make instrument here
let instrument2: Instrument = // Make instrument here
let something: Any!
// Running the code synchronously will make the program wait until the block below finishes before proceeding
instrument1.dispatchQueue.sync {
something = // Get value from instrument1
}
instrument2.dispatchQueue.async {
instrument2.doSomething(something)
}
This code will work, however, this causes the main thread to freeze during the call to sync
. This completely defeats the point of using multithreading because the main thread will still freeze during the communication with instrument1
. Instead, to get this to work properly, both operations should be completed on the same thread:
instrument1.dispatchQueue.async {
let something = // Get value from instrument1
// It is okay to call methods on instrument2 from another instrument's thread
instrument2.doSomething(something)
}
If you would like to execute more operations on instrument1
after the call to instrument2.doSomething(something)
, then you can wrap the call to doSomething(_:)
in another async block:
instrument1.dispatchQueue.async {
let something = // Get value from instrument1
instrument2.dispatchQueue.async {
instrument2.doSomething(something)
}
instrument1.doSomethingElse()
}
You can also run operations on instruments off of a custom dispatch queue:
let instrument1 = // Make instrument here
let instrument2 = // Make instrument here
let dispatchQueue = DispatchQueue(label: "Custom Queue", qos: .utility)
dispatchQueue.async {
instrument1.doSomething()
instrument2.doSomething()
}
This is especially useful if you would like to create a dispatch queue with a different quality of service (all dispatch queues accessed from the dispatchQueue
property have the .userInitiated
quality of service).
If you are using multiple queues (including the main thread) to execute operations for a single instrument be careful not to call multiple operations at once. Doing so can cause your program to crash. The safest way to prevent this is to only call operations on a single instrument off of a single dispatch queue (usually either the main thread or the dispatch queue from the dispatchQueue
property.