Enter the world of python_day35_network programming - knowledge about threads, GIL locks, mutexes, event events, pools, coroutines

Last Friday's homework - multi-process to achieve TCP server concurrency

# Server
import socket
from multiprocessing import Process


def get_server():
    server = socket.socket()
    server.bind(('127.0.0.1', 9999))
    server.listen(5)
    return server  # Pass the socket object with encapsulated properties to other functions


def get_talk(sock):
    while True:
        data = sock.recv(1024)
        print(data.decode('utf8'))
        sock.send(data.upper())


if __name__ == '__main__':
    server = get_server()  # Get the socket object
    while True:
        sock, addr = server.accept()  # Establish a channel and get the client address
        # Open multiple processes to chat
        p = Process(target=get_talk, args=(sock,))
        p.start()
 _______________________________
# There is no difference in the client, just write the normal version in the same way

1. Mutual exclusion lock - Lock, mutex

Change concurrency and parallelism into serial, sacrificing efficiency but improving data security

from multiprocessing import Process,Lock

lock making
mutex = Lock()

with a lock

mutex.acquire()  # To grab a lock, it must be placed in the operation data layer
mutex.release()  # put the lock

2. Thread theory

​ Process - resource unit

​ Thread - Execution Unit

​ There is at least one thread inside a process! ! !

​ The process is to open up memory space, and the thread is executed on the process without occupying the memory space, so the resource occupation of creating a process is much larger than that of creating a process

from threading import Thread
import time


def a(name):
    time.sleep(0.01)  # Because the thread execution is fast, try adding a block
    print(f'{name} running')
    time.sleep(3)
    print(f'{name} Finished')


if __name__ == '__main__':
    t = Thread(target=a, args=('jack',))
    t.start()
    print('The main thread executes')
    
>>>
The main thread executes
jack running
jack Finished

Three, many methods of thread

1.join method

​ same as process

2. Multiple thread data sharing under the same process

​ Because there are not multiple copy memory spaces, it is shared data

3. Thread name

​ Similar to the process ID, current_thread

4. Thread process number

​ Multiple threads under the same process have a process number

5. Count the number of threads under the same process

​ activeCount

​ Note that if you do not manually add blocking, use the for loop to generate multiple threads, and each cycle generates a thread that is executed and disappears

6. Daemon thread

​ same as daemon

7. Mutex lock

​ There is also a Lock submodule, the usage is the same

Four, GIL global interpreter lock - only in CPython

1 Introduction

1. There is a global interpreter lock (GIL) in the CPython interpreter
​ There are many types of python interpreters
​ CPython JPython PyPython (commonly used is the CPython interpreter)

2.GIL is essentially a mutex used to prevent multiple threads in the same process from executing at the same time (important)

3.GIL exists because memory management in the CPython interpreter is not thread-safe (garbage collection mechanism)

2. How to bypass the GIL

from threading import Thread
import time

num = 1


def a():
    global num
    c = num
    time.sleep(0.5)  # In fact, this is similar to grabbing tickets, because it is multi-threaded, let the threads sleep for a while, and re-created a variable name, forcibly avoiding the GIL lock
    num = c + 1


t_list = []
for i in range(1, 100):
    t = Thread(target=a)
    t.start()
    t_list.append(t)   
for t in t_list:   # Make sure all processes are running to completion
    t.join()
print(num)
>>>
2   
# If you do not change the variable name in a, the GIL lock will be affected, and the result is 100

​ GIL can only ensure that multi-threaded data in the same process will not be messed up by the garbage collection mechanism, and cannot ensure the safety of data in the program

Verify that python multithreading is useful

According to the situation

1.cpu situation

​ single cpu
​ Multiple CPUs

2. Code situation

​io-intensive (the code has io operations)
​ Computationally intensive (the code has no io)

3. Single cpu

  • io-intensive

1. Multi-process

​ Total time consumption (time consumption of a single process + io + application space + copy code)

2. Multithreading

​ Total time consumption (time consumption of a single process plus +io)

​! ! ! Advantages of multithreading

  • Computationally intensive

1. Multi-process

​ Applying for additional space consumes more resources (total time-consuming + application space + copy code + switching)

2. Multithreading

​ Relatively less time-consuming resources Through multi-channel technology (total time-consuming + switching)

​! ! ! Advantages of multithreading

4. Multiple CPUs

  • io-intensive

1. Multi-process

​ Total time consumption (time consumption of a single process + io + application space + copy code)

2. Multithreading

​ Total time consumption (single process time consumption + io)

​! ! ! Advantages of multithreading

  • Computationally intensive

1. Multi-process

​ Total time consumption (time consumption of a single process)

2. Multithreading

​ Total time-consuming (combination of multiple processes)

​! ! ! Multi-process wins!

Five, semaphore

In python concurrent programming, semaphores are equivalent to multiple mutex locks (for example, public toilets)

In other knowledge, it means that reaching a certain condition automatically triggers other operations

from threading import Thread, Lock, Semaphore
import time
import random


sp = Semaphore(5)  # Generate five locks at once


class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()


for i in range(20):
    t = MyThread()
    t.start()
    
# It is equivalent to asking 20 people to squeeze the toilet. There are only five toilets, and new people can go in and use any one of them.

Six, event event - you can suspend the thread or process

from threading import Thread, Event
import time

event = Event()  # Create an event object


def light():
    print('Red street light with red light on, waiting for green light>>>')
    time.sleep(1)
    print('3')
    time.sleep(1)
    print('2')
    time.sleep(1)
    print('1')
    time.sleep(1)
    print('The green light is on! Games start!')
    event.set()  # Let the awaited object execute immediately


def car(name):
    print(f'{name} Waiting at the traffic light at the starting line')
    event.wait()  # Make processes spawned from this function wait
    print(f'Games start! {name}sprint! ')


t = Thread(target=light)
t.start() # The following code is executed during the sleep period of the process generated by this function, and the process is generated immediately

for i in range(1, 10):
    t = Thread(target=car, args=(f'Marshmallow SR{i}',))
    t.start()
    
>>>

Red street light with red light on, waiting for green light>>>
Marshmallow SR1 Waiting at the traffic light at the starting line
 Marshmallow SR2 Waiting at the traffic light at the starting line
 Marshmallow SR3 Waiting at the traffic light at the starting line
 Marshmallow SR4 Waiting at the traffic light at the starting line
 Marshmallow SR5 Waiting at the traffic light at the starting line
 Marshmallow SR6 Waiting at the traffic light at the starting line
 Marshmallow SR7 Waiting at the traffic light at the starting line
 Marshmallow SR8 Waiting at the traffic light at the starting line
 Marshmallow SR9 Waiting at the traffic light at the starting line
3
2
1
 The green light is on! Games start!
Games start!  Marshmallow SR4 sprint! 
Games start!  Marshmallow SR5 sprint! 
Games start!  Marshmallow SR2 sprint! 
Games start!  Marshmallow SR1 sprint! 
Games start!  Marshmallow SR8 sprint! 
Games start!  Marshmallow SR6 sprint! 
Games start!  Marshmallow SR3 sprint! 
Games start!  Marshmallow SR7 sprint! 
Games start!  Marshmallow SR9 sprint!

Seven, process pool, thread pool

​ The maximum number of processes and threads can be set in advance, which can prevent the computer from being stuck due to too many processes or threads

​ Of course, because of the restrictions, the execution efficiency of the program is reduced

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import time

# 1 yields a fixed number of threads
p = ProcessPoolExecutor(12)
# pool = ThreadPoolExecutor(10)

def test(name):
    print(f'Test is running Tester{name}>>>')
    time.sleep(2)
    print(f'Test Execution Completed Tester{name}>>>')
    return 'job done'


def func(a):
    print('I will be responsible for collecting the results')
    print(a.result()) # To print the return value, use result()


if __name__ == '__main__': # Process don't forget main

    for i in range(1, 24):
        # Directly stuff tasks into the pool, and the pool creates processes in batches according to the set number
        p.submit(test, 'jack').add_done_callback(func) # Get the result of the process execution by calling the function that specifically receives the result

Eight, coroutine - gevent third-party module

​ Because the CPU is the core of the work, and if an io operation is encountered during program execution, the CPU will leave temporarily. The coroutine technology can deceive the CPU to pretend that there is no io operation in the code, allowing the CPU to follow along all the time to achieve maximum efficiency. Purpose.

​ This module does not need to understand the underlying principles. This is a method packaged by other big guys, so you can use it

from gevent import monkey;
monkey.patch_all()  # Fixed syntax for detecting all IO operations (monkey patch)
from gevent import spawn
import time


def a():
    print('a running')
    time.sleep(2)
    print('a end of execution')


def b():
    print('b running')
    time.sleep(3)
    print('b end of execution')


if __name__ == '__main__':
    start_time = time.time()
    a()
    b()
    print(time.time() - start_time)# >>> 5.056353569030762 
    
----------------------------
if __name__ == '__main__':
    start_time = time.time()
    s1 = spawn(a)
    s2 = spawn(b)
    s1.join()
    s2.join()
    print(time.time() - start_time) # >>>3.0264976024627686
    

Nine, single thread to achieve high concurrency

# Server
from gevent import monkey;
monkey.patch_all()
from gevent import spawn
import socket

def a(sock):
    while True:
        date = sock.recv(1024)
        print(date.decode('utf8'))
        sock.send(date.upper())


def s():
    sever = socket.socket()
    sever.bind(('127.0.0.1', 8088))
    sever.listen(5)
    while True:
        sock, addr = sever.accept()
        spawn(a, sock)


s1 = spawn(s)
s1.join()

--------------------------
# client
import socket

c1 = socket.socket()
c1.connect(('127.0.0.1', 8088))

while True:
    c1.send(b'hello world')
    date = c1.recv(1024)
    print(date.decode('utf8'))
# Remember to open the repeated execution of a single Py file in pycharm


↑↑↑↑↑↑↑↑

Posted by Distant_storm on Mon, 21 Nov 2022 18:02:12 +0530