3.5. Threading Synchronization
Thread Synchronisation
3.5.1. Problems
Lock Contention - when there is a shared resource which is wanted by many threads very often. Most of the threads will wait for access to that resource
Lock Starvation - some threads are more lucky to get more access than the others. There are even some threads which didn't get a lock at all!
Deadlock - You cannot access a lock to get a resource, because you haven't acquired a resource in the first place (example: the pharmacy won't sell you mask, because you are not wearing a mask)
The more lock, the slower your program is and more memory it uses
3.5.2. Locks
Locks don't lock anything. They are just flags and can be ignored. It is a cooperative tool, not an enforced tool
IIn general, locks should be considered a low level primitive that is difficult to reason about nontrivial examples. For more complex applications, you're almost always better of with using atomic message queues.
The more locks you acquire at one time, the more you loose the advantages of concurrency
Source: [2]
3.5.3. GIL
Global Interpreter Lock
CPython has a lock for its internal shared global state
One lock instead of hundreds smaller
The unfortunate effect of GIL is that no more than one thread can run at a time
For I/O bound applications, GIL doesn't present much of an issue
For CPU bound applications, using threads makes the application speed worse
Accordingly, that drives us to multiprocessing to gain more CPU cycles
Larry Hastings, Gilectomy project - removed GIL, but Python slowed down
Source: [2]
3.5.4. Thread Local Storage
Operating system mechanism
Limits global variables to be seen only by current thread
You can keep data around, which are specifically not shared with other threads
3.5.5. Example
from threading import Thread
class MyThread(Thread):
def run(self):
print('hello')
t1 = MyThread()
t1.start()
t2 = MyThread()
t2.start()
t1.join()
t2.join()
from threading import Thread
RUNNING = []
class MyThread(Thread):
def run(self):
print('hello')
t1 = MyThread()
t1.start()
RUNNING.append(t1)
t2 = MyThread()
t2.start()
RUNNING.append(t2)
for thread in RUNNING:
thread.join()
from threading import Thread
RUNNING = []
class MyThread(Thread):
def run(self):
print('hello')
def spawn(cls, count=1):
for i in range(count):
t = cls()
t.start()
RUNNING.append(t)
spawn(MyThread, count=10)
for thread in RUNNING:
thread.join()