How to synchronize threads with shared global variable in python? - python

i'm currently trying to unterstand threading in python and i wrote a program that ideally would have 2 threads alternating between incrementing and decrementing a global variable but no matter how i spread out the lock it inevitably becomes out of sync.
number = 0
lock = threading.Lock()
def func1():
global number
global lock
while True:
try:
lock.acquire()
number += 1
finally:
lock.release()
print(f"number 1 is: {number}")
time.sleep(0.1)
def func2():
global number
global lock
while True:
try:
lock.acquire()
number -= 1
finally:
lock.release()
print(f"number 2 is: {number}")
time.sleep(0.1)
t1 = threading.Thread(target=func1)
t1.start()
t2 = threading.Thread(target=func2)
t2.start()
t1.join()
t2.join()
the output should look something like this:
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
but right now it looks like this:
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 2 is: -1number 1 is: 0
number 2 is: -1number 1 is: 0
number 1 is: 1number 2 is: 0
any idea how to do this without falling out of sync?

First, avoid using global variables with threads in python. Use a queue to share the variables instead.
Second, the lock acquisition in non-deterministic. At the moment a lock is released, you have no guarantee that the other thread will grab it. There is always a certain probability that the thread that just released the lock can grab it again before the other thread.
But in your case, you can avoid problems because you know the state that the variable needs to be to accept modifications by one thread or the other. So, you can enforce the protection for modification by verifying if the variable is in the right state to accept a modification.
Something like:
from threading import Thread
import time
from queue import Queue
def func1(threadname, q):
while True:
number = q.get()
if number == 0:
number += 1
print(f"number 1 is: {number}")
q.put(number)
time.sleep(0.1)
def func2(threadname, q):
while True:
number = q.get()
if number == 1:
number -= 1
print(f"number 2 is: {number}")
q.put(number)
time.sleep(0.1)
queue = Queue()
queue.put(0)
t1 = Thread(target=func1, args=("Thread-1", queue))
t2 = Thread(target=func2, args=("Thread-2", queue))
t1.start()
t2.start()
t1.join()
t2.join()

thanks for all your answers, i remember seing someone in the comments mentioned using events or something like that and that solved the issue. here's the code:
number = 0
event_number = threading.Event()
event_number.clear()
def func1():
global number
global event_number
while True:
if not event_number.is_set():
number += 1
print(f"func 1 is {number}")
event_number.set()
else:
pass
time.sleep(2)
def func2():
global number
global event_number
while True:
if event_number.is_set():
number -= 1
print(f"func 2 is {number}")
event_number.clear()
else:
pass
time.sleep(2)
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
t1.start()
t2.start()
t1.join()
t2.join()
now i notice that sometimes one of the loops will either not wait it's alloted time and print right away or wait double the time but at least the number only stays within those 2 values.

For starters, time.sleep is not exactly accurate. And depending on the python-implementation you're using (most likely cpython) multithreading might not quite work the way you're expecting it to. These two factors allow the initially correct timing of your threads to get out of sync within fairly short time.
There solution for this problem is to enforce alternate operation on the variable by the two threads via two locks:
import time
import threading
var = 0
def runner(op, waitfor, release):
global var
while True:
try:
# wait for resource to free up
waitfor.acquire()
# operation
var = op(var)
print(f"var={var}")
finally:
# notify other thread
release.release()
time.sleep(0.1)
# init locks for thread-synchronization
lock_a = threading.Lock()
lock_b = threading.Lock()
lock_a.acquire()
lock_b.acquire()
# create and start threads (they'll wait for their lock to be freed)
thread_a = threading.Thread(target=runner, args=(lambda v: v - 1, lock_a, lock_b))
thread_b = threading.Thread(target=runner, args=(lambda v: v + 1, lock_b, lock_a))
thread_a.start()
thread_b.start()
# let thread_b start the first operation by releasing the lock
lock_b.release()
In the above code, each thread has a lock that can be used to notify it, that the resource may be used by it. Thus threads can hand control over the global variable to each other.

Related

Threading with priority queue

I wanted to test threads in python, so I tried to create 3 threads that run at the same time but each one prints a different letter from A - C.
It was easy so I added that the letters would be printed from first to last and I'm getting confused from here.
How can I make 3 threads that run talk to each other and to know when to print and when to not print?
the output should be:
A B C A B C A B C A B C
my code:
import threading
import time
import queue
val = 0
def increment(letter):
global val
for x in range(100):
val += 1
lock.acquire()
time.sleep(0.5)
print(val, letter, ' ')
lock.release()
lock = threading.Lock()
thread1 = threading.Thread(target=increment, args=('A',))
thread2 = threading.Thread(target=increment, args=('B',))
thread3 = threading.Thread(target=increment, args=('C',))
thread1.start()
thread2.start()
thread3.start()

Threading infinity loops and sync problem

I am getting headache, trying to figure out how to deal with 2 threads in Python.
My Idea was to create one thread for blinking LEDs, that have infinite while loop, and when global variable changes to True,it starts to blink etc, but it is always in infinite loop.
other main thread, will be waiting for signal to blink, but also if the signal is while blinking, it will reset global blinking timer, so it has to be in separate thread.
Problem I am having, when I start LEDs thread from main thread, because LED thread has infinite loop, it stops main thread from running any further than flashThread.start()
if __name__ == "__main__":
print('starting main...')
flashThread = threading.Thread(name='SFlashing', daemon = True, target=Flash)
flashThread.start()
while(True):
print('running main...')
time.sleep(2)
it never gets to running main...
Flash() is just infinite while loop and depending on global variables it does stuff...
Here is an example of a main thread printing running main... + a parallel thread incrementing a variable count, based on your explanations. Your code should work, maybe could you give us more information about the Flash loop so we can help you ?
import threading
import time
def Flash():
count = 0
while True:
print("count", count)
count += 1
time.sleep(1)
print('starting main...')
flashThread = threading.Thread(name='SFlashing', daemon = True, target=Flash)
flashThread.start()
while(True):
print('running main...')
time.sleep(2)
Output:
starting main...
count 0
running main...
count 1
running main...
count 2
count 3
running main...
** EDIT **
Here there is no more time.sleep()in Flash, until the global variable totois changed in the main loop.
import threading
import time
global toto
toto = 0
def Flash():
global toto
count = 0
while True:
print("count", count)
count += 1
if toto == 2:
print("YEEHA")
time.sleep(2)
print('starting main...')
flashThread = threading.Thread(name='SFlashing', daemon = True, target=Flash)
flashThread.start()
while(True):
print('running main...')
time.sleep(1)
toto = 2

Threads share cpu only if sleep

So I have this code:
import threading
from time import sleep
counter = 0
TOTAL = 10_000
def some_work(name):
global counter
print("Doing work from :", name)
while counter<TOTAL:
if counter % 1000 == 0:
print("work from :", name)
counter+=1
sleep(0.001) #<--- This right here
num_workers = 2
threads = []
for worker in range(num_workers):
work = lambda :some_work(worker)
thread = threading.Thread(target=work, daemon=True)
threads.append(thread)
thread.start()
sleep(0.5)
[t.join() for t in threads]
Which gives me a nice output of asynchronous work. The thing is if I comment out the annotated sleep(0.001) , then only one thread works. The other one jumps in only after the first one has finished. The output to that is :
Doing work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
work from : 0
Doing work from : 1
I have larger piece of code where the same phenomenon occurs with or without sleep. (No reason to show that code , it would be too large). I would just like to know when does this happen, a thread hogs all the cpu to itself and everything actually works sequentially?

How to measure time taken of multi-threads created in a loop?

I want to measure how much time it takes to finish running the code with multiple threads in python.
If I put join inside the loop, it will stop the loop (main thread) from keep creating new threads. It will run the sleep() one by one.
If I put join on the thread which I use to create thread_testing, the join won't work somehow. It prints out the time immediately.
def sleep(name):
print("{} going to sleep".format(name))
time.sleep(5)
print("{} wakes up after 5 seconds".format(name))
def thread_testing():
for i in range(3):
t = threading.Thread(target=sleep, name='thread' + str(i), args=(i,)
t.start()
# t.join() #1
if __name__ == '__main__':
start = time.time()
t = threading.Thread(target=thread_testing, name='threadx')
t.start()
t.join() #2
print(time.time() - start)
Desired output:
1 sleep
2 sleep
3 sleep
1 wake up after 5
2 wake up after 5
3 wake up after 5
5.xxx secs
Join will wait for your thread. That is why your threads were executed one by one.
What you have to do is:
Start all threads
Store them somewhere
Once everything is started wait for every thread to finish.
Assuming you don't need the first thread started in main:
import time
import threading
def sleep(name):
print("{} going to sleep".format(name))
time.sleep(5)
print("{} wakes up after 5 seconds".format(name))
def thread_testing():
threads = []
for i in range(3):
t = threading.Thread(target=sleep, name='thread' + str(i), args=(i,))
t.start()
threads.append(t)
for t in threads:
t.join()
if __name__ == '__main__':
start = time.time()
thread_testing()
print(time.time() - start)

python multithreading - confused with output

I am trying to create a multithreading program that will run one thread for three seconds, then 'pause' the thread using event objects, wait for a few seconds, then repeat the process over again. here is my code:
import threading
import time
counter = 1
def control():
global counter
test_event.wait()
while(test_event.is_set()):
print 'waiting 3 seconds'
time.sleep(3)
event.set()
time.sleep(3)
event.clear()
if(counter == 1):
counter = counter - 1
def function():
global counter
event.wait()
while (event.is_set()):
test_event.clear()
print 'event is set to high'
time.sleep(1)
print 'event is set to low'
test_event.set()
event = threading.Event()
test_event = threading.Event()
t1 = threading.Thread(target = control)
t2 = threading.Thread(target = function)
t1.start()
t2.start()
while(counter == 1):
test_event.set()
the output I am desiring should look like this:
waiting 3 seconds
event is set to high
event is set to high
event is set to high
event is set to low
waiting 3 seconds
event is set to high
....
....
and that keeps repeating.
the output I am currently receiving however is this:
waiting three seconds
event is set to high
event is set to high
event is set to high
waiting 3 seconds
event is set to low
waiting 3 seconds
waiting 3 seconds
waiting 3 seconds
...
...
and that keeps repeating.
Can anybody detect where I am messing up, and possibly offer any advice for me to fix? I cannot seem to figure out what I am doing wrong.
P.S. I am aware that this code probably is constructed very poorly, but I am completely new to multithreading...sorry in advance!

Categories

Resources