I want to figure out how to perform functions in different classes at the same time. When I try to do this using threading , I see that the functions are executed in order. Below is a simple example of what I'm trying to do. In general, I try to make a program that receives data, writes it to the database and simultaneously draws graphs from the database online. How to make several functions run in different classes at the same time I do not understand.

 import threading from threading import Thread class f1: def __init__(self): pass def func1(self): for i in range(10): print 'Working111' class f2: def __init__(self): pass def func2(self): for i in range(10): print 'Working222' ff1 = f1() ff2 = f2() if __name__ == '__main__': Thread(target = ff1.func1()).start() Thread(target = ff2.func2()).start() 

Conclusion:

 >>> Working111 Working111 Working111 Working111 Working111 Working111 Working111 Working111 Working111 Working111 Working222 Working222 Working222 Working222 Working222 Working222 Working222 Working222 Working222 Working222 >>> 

    4 answers 4

    As @Avernial correctly noted, in the standard Python implementation, CPython, GIL takes place, the presence of which blocks the parallel execution of threads in Python programs. But at the same time, no one forbids using processes for parallelism.

    A fairly convenient way to implement the launch of parallel code in processes is provided by the standard multiprocessing library. Below is a sample code adapted to your:

     from multiprocessing import Process from time import sleep class A: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class A, i=%s' % i) sleep(sleep_time) class B: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class B, i=%s' % i) sleep(sleep_time) if __name__ == '__main__': a = A() b = B() p1 = Process(target=a, kwargs={'sleep_time': 0.7}) p2 = Process(target=b, args=(12,)) p1.start() p2.start() p1.join() p2.join() 

    The output may look something like this:

     Working class A, i=0 Working class B, i=0 Working class B, i=1 Working class A, i=1 Working class B, i=2 Working class A, i=2 Working class B, i=3 Working class B, i=4 Working class A, i=3 Working class B, i=5 Working class A, i=4 Working class B, i=6 Working class A, i=5 Working class B, i=7 Working class B, i=8 Working class A, i=6 Working class B, i=9 Working class A, i=7 Working class B, i=10 Working class B, i=11 Working class A, i=8 Working class A, i=9 

    I note that if you use the capabilities of the package threading on the same code, ie

     import threading import os from time import sleep class A: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class A, i=%s' % i) sleep(sleep_time) class B: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class B, i=%s' % i) sleep(sleep_time) if __name__ == '__main__': a = A() b = B() t1 = threading.Thread(target=a, kwargs={'sleep_time': 0.7}) t2 = threading.Thread(target=b, args=(12,)) t1.start() t2.start() t1.join() t2.join() 

    then the output will be similar to the previous one. The explanation of this effect is given below.


    You can use the threading module if at some point the calculation of the function is interrupted, for example, when reading data, waiting for user input or when calling the time.sleep() function, since in these cases the GIL is removed and other threads can intercept control.

    Here is a quote from the documentation :

    It can be used to make it easier for you to do this. If you want to use multi-core machines, you are advised to use multiprocessing or concurrent.futures.ProcessPoolExecutor . However, multiple I / O-bound tasks simultaneously .

    My free translation:

    Details of the CPython interpreter implementation: in CPython, due to the presence of GIL, only one Python code stream can be executed (even though some performance-oriented libraries can bypass this limitation). If you want to improve your application by adding the ability to use the capabilities of a multi-core computer, we suggest you use the capabilities of the multiprocessing package or concurrent.futures.ProcessPoolExecutor . However, threading is suitable if you want to run multiple I / O-bound tasks at the same time .


    I decided to give a more interesting example of comparing the work of the module threading and multiprocessing .

    We will show that if there is no I / O or other operations leading to the release of the GIL in one of the threads, then control will not be transferred.

    To do this, create two files. In one, the threading module mechanism will be used, and in the other - multiprocessing . The class B function code will perform lengthy calculations without interrupting I / O. The rest of the code will be the same.

    File threading_test.py:

     import threading import os from time import sleep class A: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class A, i=%s' % i) sleep(sleep_time) class B: def __call__(self, count=10, sleep_time=0.5): for i in range(count): x = 10 for j in range(50000): x *= 10 ** 9 # какая-то долгая операция print('Working class B, i=%s' % i) if __name__ == '__main__': a = A() b = B() t1 = threading.Thread(target=a, kwargs={'sleep_time': 0.7}) t2 = threading.Thread(target=b, args=(12,)) t1.start() t2.start() t1.join() t2.join() 

    Multiprocessing_test.py file:

     from multiprocessing import Process import os from time import sleep class A: def __call__(self, count=10, sleep_time=0.5): for i in range(count): print('Working class A, i=%s' % i) sleep(sleep_time) class B: def __call__(self, count=10, sleep_time=0.5): for i in range(count): x = 10 for j in range(50000): x *= 10 ** 9 # какая-то долгая операция print('Working class B, i=%s' % i) if __name__ == '__main__': a = A() b = B() p1 = Process(target=a, kwargs={'sleep_time': 0.7}) p2 = Process(target=b, args=(12,)) p1.start() p2.start() p1.join() p2.join() 

    After running the code, we get the following output from the threading_test.py script:

     Working class A, i=0 Working class B, i=0 Working class B, i=1 Working class B, i=2 Working class B, i=3 Working class B, i=4 Working class A, i=1 Working class B, i=5 Working class B, i=6 Working class B, i=7 Working class B, i=8 Working class B, i=9 Working class B, i=10 Working class B, i=11 Working class A, i=2 Working class A, i=3 Working class A, i=4 Working class A, i=5 Working class A, i=6 Working class A, i=7 Working class A, i=8 Working class A, i=9 

    and in the multiprocess_test.py script:

     Working class A, i=0 Working class A, i=1 Working class A, i=2 Working class A, i=3 Working class B, i=0 Working class A, i=4 Working class A, i=5 Working class B, i=1 Working class A, i=6 Working class A, i=7 Working class A, i=8 Working class B, i=2 Working class A, i=9 Working class B, i=3 Working class B, i=4 Working class B, i=5 Working class B, i=6 Working class B, i=7 Working class B, i=8 Working class B, i=9 Working class B, i=10 Working class B, i=11 

    It can be noted that in the case of threading , the class B function, having taken possession of the interpreter, practically did not let it go (switching occurred only if the class A flow managed to switch to rare output operations), while in the second case, the class A function it was quickly executed and the program expected only the completion of the function of class B At the same time, no one interfered with each other and the processes were executed in parallel on different processor cores.

    The problem in question has nothing to do with GIL (as is usually the case in such cases).

    In Python, if f is a function, then the f() syntax calls this function , and the result of the f() expression is the return value ( None in your case).

    Your code is executed sequentially, because both functions are called in the main thread. Your code is equivalent to:

     ff1.func1() Thread(target=None).start() ff2.func2() Thread(target=None).start() 

    Thread(target=None).start() creates a new thread that ends immediately.

    To perform functions in parallel in your case, simply erase () :

     Thread(target=ff1.func1).start() Thread(target=ff2.func2).start() 

    Without () , you pass function objects, not their return values. To see that the output from func1 and func2 is interleaved, increase the number of iterations if the first thread has time to complete before the second one starts. Example output:

     Working111 Working222Working111 Working111 Working222 Working111 Working222 Working111Working222 Working222 Working111Working222 Working222 Working111Working222 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222 Working111 Working222Working111 Working111 Working222 ... 

    print not thread-safe, so mixing the output even on one line is expected. In some cases, new lines may even be added .

      The threading module in this case will not help you because GIL is used in python. For parallel execution, you can use multiprocessing or concurrent modules.

        I solved the parallel execution problem like this:

         from time import sleep from threading import Thread class a: def aaa(self): self.num = 0 while True: print 'aaa', self.num sleep(1) self.num += 1 class b: def bbb(self): self.num = 0 while True: print 'bbb', self.num sleep(2) self.num += 1 class c: def ccc(self): self.num = 0 while True: print 'ccc', self.num sleep(3) self.num += 1 ap = a() aa = Thread(target=ap.aaa, args=()) bp = b() bb = Thread(target=bp.bbb, args=()) cp = c() cc = Thread(target=cp.ccc, args=()) aa.start() bb.start() cc.start() aa.join() bb.join() cc.join() print'111' 

        print'111' will never be .join() as .join() blocks the execution of the main thread, and in the class method the eternal loop