Can I run multi threads working on the same function simultaneously? - python

I'm trying to run a problem using a single thread then running the same problem using multi threads to show the difference in the two times of the running execution, however, the running time appears to be the same, so I guess that is because the threads are not running simultaneously(parallel).
So if anyone can please tell me how to run them simultaneously.
import threading, time
def fib(n):
if n <= 1:
return 1
return fib(n - 1) + fib(n - 2)
def fib_caller(l):
global list
for i in range(10):
x = fib(l[i])
print(x)
list[i] = x
if __name__ == '__main__':
list = [1, 37, 1, 37, 1, 37, 1, 37, 1, 37]
choice = input(
"Please choose whether to :\nSingle-threaded process : Enter s\nor\nMulti-threaded
process : Enter m\n")
begin = time.time()
if choice == 's' or 'S':
t = threading.Thread(name="Single thread", target=fib_caller, args=(list, ))
t.start()
t.join()
elif choice == 'm' or 'M':
t1 = threading.Thread(name="Single thread", target=fib_caller, args=(list[0:2]))
t1.start()
t1.join()
t2 = threading.Thread(name="Single thread", target=fib_caller, args=(list[2:4]))
t2.start()
t2.join()
t3 = threading.Thread(name="Single thread", target=fib_caller, args=(list[4:6]))
t3.start()
t3.join()
t4 = threading.Thread(name="Single thread", target=fib_caller, args=(list[6:8]))
t4.start()
t4.join()
t5 = threading.Thread(name="Single thread", target=fib_caller, args=(list[8:10]))
t5.start()
t5.join()
else:
print('Invalid Input.')
print(list)
end = time.time()
total = end - begin
print("Total execution time: " + str(total))

This is direct from the threading library documentation.
"CPython implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously."

Related

python problem using multithread with blocking

i have a thread running on the background checking every 5 sec and print a msg after 5 secs passes.
in loop1 the msg should appear at specific point. (in this case, its above print('test')).
the thread can wait longer than 5 secs when loop1 is running (like a few sec delay is fine) but it have not to appear at the same time as loop1 is running so i put queue to for blocking.
when loop2 is running, the messsage in the multithread should appear while loop2 is running at the same time unlike loop no1.
so i didnt put queue.get in loop2 since theres no need for blocking but the problem is when going into loop3. if we go into loop3 while we are on time.sleep(5) in multithread, thats fine but if we are on print ("thread working2") in multithread, i want to make sure it waits until it does all the work in print ("thread working2") part. there are more codes there but i just put a simple print for better readability. before going into loop3. is there any way i can accomplish this? and is the method im using right now is suitable for my intention?
sorry for my bad english!
import threading
import time
from queue import Queue
queue = Queue()
queue2 = Queue()
switch = False
def func0():
while True:
global switch
global queue
global queue2
time.sleep(5)
switch = True
print ("switch on")
a = queue.get()
if a == 1:
print("thread working")
time.sleep(0.5)
print("thread working")
time.sleep(0.5)
switch = False
queue2.put(True)
if a == 2:
print("thread working2")
time.sleep(0.5)
print("thread working2")
time.sleep(0.5)
switch = False
if __name__ == "__main__":
t1 = threading.Thread(target=func0)
t1.start()
testnumber = 0
testnumber2 = 0
testnumber3 = 0
while True: #loop no.1
if switch:
queue.put(1)
queue2.get()
print ("loop1")
time.sleep(0.5)
testnumber = testnumber +1
if testnumber == 200:
break
while True: #loop no.2
if switch:
queue.put(2)
print ("loop2")
time.sleep(0.5)
testnumber2 = testnumber2 + 1
if testnumber2 == 200:
break
while True: #loop no.3
print ("loop3")
time.sleep(0.5)
testnumber3 = testnumber3 + 1
if testnumber3 == 200:
break
t1.join()

what difference between these two join methods in threading in python?

I want to use threading package to calculate the square of num and my code like,
import threading
def my_squr(num): #if this function take long time to run
print(num*num)
return num*num
if __name__ == "__main__":
l1 = [1,3,5,7,11,13,15,17]
for i, item in enumerate(l1):
if i % 3 == 0:
t1 = threading.Thread(target=my_squr, args=(item,))
t1.start()
t1.join()
elif i % 3 == 1:
t2 = threading.Thread(target=my_squr, args=(item,))
t2.start()
t2.join()
else:
t3 = threading.Thread(target=my_squr, args=(item,))
t3.start()
t3.join()
# t1.join()
# t2.join()
# t3.join()
print("Done")
However, I am confused about where should I put the join() method.Although, they both get same answer, I guess there are some differeces between them.
If you immediately join after started an thread, it means that wait until it executes. However, this isn't different than calling function normally inside main thread. Assume that functions works takes a bit of time and you need to process them at the same time.
Then you can start them and uncomment joins.
This is your current code snippet workflow
->Create thread x and start
->wait for finish of thread x
->Create thread y and start
->wait for finish of thread y
... and so on.
However if you change comments of joins this is new workflow
->Create thread x and start
->Create thread y and start
->Create thread z and start
...
at the end
->wait thread x to finish
->wait thread y to finish
...
So here even when you are waiting to finish X, your other threads like y and z still processing whatever you are doing inside.
EDIT:
You should remove the joins where right after the start and uncomment threads that are been in the end. That would be a more appropriate. Also, as processors are fast enough to complete simple math just in a millisecond, you will not experience any difference.
Where u should use the joins is completely dependent on your program.
For your situation using at the end probably would be the best.
However, assume you will have another thread called X_2 that will use result of thread X_1. Then before creating thread X_2, you should join thread X_1.
You could construct a list of the threads then join them all once your loop terminates:
import threading
def my_squr(num): #if this function take long time to run
print(num*num)
return num*num
if __name__ == "__main__":
threads = []
l1 = [1,3,5,7,11,13,15,17]
for i, item in enumerate(l1):
if i % 3 == 0:
t1 = threading.Thread(target=my_squr, args=(item,))
t1.start()
threads.append(t1)
elif i % 3 == 1:
t2 = threading.Thread(target=my_squr, args=(item,))
t2.start()
threads.append(t2)
else:
t3 = threading.Thread(target=my_squr, args=(item,))
t3.start()
threads.append(t3)
for thread in threads:
thread.join()
print("Done")
Join simply stops the application from ending before the thread completes.
So you want to join threads AFTER they have started.
import threading
def my_squr(num): #if this function take long time to run
print(num*num)
return num*num
if __name__ == "__main__":
threads = list()
l1 = [1,3,5,7,11,13,15,17]
for i, item in enumerate(l1):
if i % 3 == 0:
t1 = threading.Thread(target=my_squr, args=(item,))
threads.append(t1)
t1.start()
elif i % 3 == 1:
t2 = threading.Thread(target=my_squr, args=(item,))
threads.append(t2)
t2.start()
else:
t3 = threading.Thread(target=my_squr, args=(item,))
threads.append(t3)
t3.start()
for t in threads:
t,join()
print("Done")

Multi-threading and events exercise in Python

"Write a program using two threads such that one writes even numbers in increasing order and the other odd numbers in incresing order with respect to a certain threashold."
For instance, given 10 I would like to have as output
T1-0
T2-1
T1-2
T2-3
...
T1-8
T2-9
I think an event object should be used in order to alternate between the print of a thread with the other, but I do not know how to implement it in order to make it work, since I think I have not fully grasped the tools to work with threads, yet. I leave here my faulty code for the task
import threading
e = threading.Event()
def even(n):
if e.isSet() == False:
for i in range(0,n,+2):
e.set()
print(i)
e.clear()
e.wait()
def odd(n):
for i in range(1,n,+2):
e.wait()
print(i)
e.set()
t1 = threading.Thread(target = odd, args=(10,))
t2 = threading.Thread(target = even, args=(10,))
t1.start()
t2.start()
t1.join()
t2.join()
import threading
import time
def thread_function(name,max,y):
print(name,y)
time.sleep(0.1)
for i in range(int(max/2)):
time.sleep(0.3)
y= y + 2
print(name,y)
n = input("Input an integer \n")
n = int(n)
p=0
# Create thread
mt1 = threading.Thread(target=thread_function, args=("T-Even",n,p))
mt2 = threading.Thread(target=thread_function, args=("T-Odd",n,p+1))
# Start thread
mt1.start()
time.sleep(0.1)
mt2.start()
mt1.join()

How to create a lock for a specific variable in main while using queues in threads

I am trying to figure out how in python one would declare a specific variable to be locked so that only one thread may access it at a time to avoid race conditions. If I have two threads constantly updating a variable via queues, but I am also updating the variable manually in main, what would be the right way to declare that variable a shared resource by all threads so that only one may access it at a time between the threads being run and main?
I wrote a little example code to show what I mean.
import time
from random import randint
from threading import Thread
from queue import Queue
# Add the amount by random number from 1 - 3 every second
def producer(queue, amount):
while True:
time.sleep(1)
amount[0] += randint(1, 3)
queue.put(amount)
# Subtract the amount by random number from 1 - 3 every second
def consumer(queue, amount):
while True:
item = queue.get()
amount[0] -= randint(1, 3)
queue.task_done()
amount = [10]
queue = Queue()
t1 = Thread(target=producer, args=(queue, amount,))
t2 = Thread(target=consumer, args=(queue, amount,))
t1.start()
t2.start()
while True:
n = input("Type a number or q: ")
if n == 'q':
break
else:
# Here is where I am confused about how to declare the amount a
# shared resource and lock it in a way that the queues would also
# adhere to
amount[0] += int(n)
print("amount is now: {}".format(amount[0]))
t1.join()
t2.join()
It is important to lock the variable when you are updating the value of it. so in your case indeed you require locking mechanism.
How to lock:
create a threading.Lock object which will help you lock and release the block of code.
acquire : to lock the code block. only one thread can enter in this block. other thread will wait until it is released.
release : to release the acquired lock.
In your case:
import time
from random import randint
from threading import Thread,Lock
from queue import Queue
# Add the amount by random number from 1 - 3 every second
def producer(queue, amount,lock):
while True:
time.sleep(1)
lock.acquire()
amount[0] += randint(1, 3)
queue.put(amount)
lock.release()
# Subtract the amount by random number from 1 - 3 every second
def consumer(queue, amount,lock):
while True:
lock.acquire()
item = queue.get()
amount[0] -= randint(1, 3)
queue.task_done()
lock.release()
amount = [10]
lock = Lock()
queue = Queue()
t1 = Thread(target=producer, args=(queue, amount,lock))
t2 = Thread(target=consumer, args=(queue, amount,lock))
t1.start()
t2.start()
...

Python threading: second thread waits until the first one finishes

I'm quite new to Python threading, and still can't make it working properly. I do not understand why, but threads are executed consequently and not in a parallel.
Could anyone please advice, what is incorrect in the code (I simplified it as much as I can to get it closer to the examples, but it doesn't work as expected):
import threading, time
def func1():
for j in range (0, 10):
print(str(time.ctime(time.time())) + " 1")
time.sleep(0.5)
def func2():
for j in range (0, 10):
print(str(time.ctime(time.time())) + " 2")
time.sleep(0.5)
print(str(time.ctime(time.time())) + " script started")
t1 = threading.Thread(target = func1(), name = " 1")
t2 = threading.Thread(target = func2(), name = " 2")
t1.start()
t2.start()
t1.join()
t2.join()
print (str(time.ctime(time.time())) + " over")
In console output I see that the second thread only starts when the first one is finished. I've tried to make threads daemonic, remove .join() lines, but still no luck.
I want to indicate the fact that a threading.Lock object and condition synchronization objects they define are used with the "with statement," because they support the context management protocol:
lock = threading.Lock() # After: import threading
with lock:
# critical section of code
...access shared resources...
Here, the context management machinery guarantees that the lock is automatically acquired before the block is executed and released once the block is complete, regardless of exception outcomes.
Therefore, the suggested solution above by Vincent seems to be addressing a more complex problem, one that deals with placing a lock on shared common resources, stopping any other thread that tries to access the resource in its tracks (in fact, stopping any thread that attempts to acquire the same lock). Notes: A threading.Lock has two states: locked and unlocked, and it is created in the unlocked state. In the following, for example, as only one thread can update the global variable "count":
import threading, time
count = 0
def adder(addlock): # shared lock object passed in
global count
with addlock:
count = count + 1 # auto acquire/release around stmt
time.sleep(0.5)
with addlock:
count = count + 1 # only 1 thread updating at any time
addlock = threading.Lock()
threads = []
for i in range(100):
thread = threading.Thread(target=adder, args=(addlock,))
thread.start()
threads.append(thread)
for thread in threads: thread.join()
print(count)
I'd suggest another solution using multiprocessing since your two parallel functions are basically two separate processes that don't need to access any shared resources.
from multiprocessing import Process
import time
def func1():
for j in range (0, 10):
print(str(time.ctime(time.time())) + " 1")
time.sleep(0.5)
def func2():
for j in range (0, 10):
print(str(time.ctime(time.time())) + " 2")
time.sleep(0.5)
if __name__ == '__main__':
print(str(time.ctime(time.time())) + " script started")
p1 = Process(target=func1)
p1.start()
p2 = Process(target=func2)
p2.start()
p1.join()
p2.join()
print (str(time.ctime(time.time())) + " over")
You're calling your targets (target=func1()). Instead do as follows:
t1 = threading.Thread(target=func1, name = "1")
t2 = threading.Thread(target=func2, name = "2")
EDIT: This is how you lock your prints :
import threading, time
def func1(lock):
for j in range (10):
with lock:
print(str(time.ctime(time.time())) + " 1")
time.sleep(0.5)
def func2(lock):
for j in range (10):
with lock:
print(str(time.ctime(time.time())) + " 2")
time.sleep(0.5)
lock = threading.Lock()
t1 = threading.Thread(target = func1, name = " 1", args=(lock,))
t2 = threading.Thread(target = func2, name = " 2", args=(lock,))

Categories