[example multitasking] Python multithreading module

Python has built-in threading module, which encapsulates the lower level thread module
for built-in methods, see the official document: threading – thread based parallelism

Multithreading execution

The main thread will wait for all the child threads to finish

#coding=utf-8
import threading
import time

def thread_test():
    print("test.")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=thread_test)
        t.start() 

View the number of threads

#coding=utf-8
import threading
from time import sleep, ctime

def a():
    for i in range(3):
        print("a...%d"%i)
        sleep(1)

def bbbbb():
    for i in range(3):
        print("bbbbb...%d"%i)
        sleep(1)

if __name__ == '__main__':
    print('---start---:%s'%ctime())
    t1 = threading.Thread(target=a)
    t2 = threading.Thread(target=bbbbb)

    t1.start()
    t2.start()

    while True:
        length = len(threading.enumerate())
        # length = threading.active_count()
        print('The number of threads currently running is: %d'%length)
        if length<=1:
            break

        sleep(0.5)

Encapsulating the threading. Thread class

Inherit threading. Thread and override run method

#coding=utf-8
import threading
import time

class MyThread(threading.Thread):

    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm "+self.name+' @ '+str(i) The name of the current thread is stored in the #name attribute
            print(msg)


if __name__ == '__main__':
    t = MyThread()
    t.start()

Thread execution order

Each thread runs a complete run function, but the start order of the thread and the execution order of each loop in the run function cannot be determined.

Sharing global variables and passing parameters

# Shared global variables
from threading import Thread
import time

g_num = 100

def work1():
    global g_num
    for i in range(3):
        g_num += 1
    print("----in work1, g_num is %d---"%g_num)


def work2():
    global g_num
    print("----in work2, g_num is %d---"%g_num)


print("---Before the thread is created g_num is %d---"%g_num)

t1 = Thread(target=work1)
t1.start()

#Delay for a while to make sure things are done in thread t1
time.sleep(1)

t2 = Thread(target=work2)
t2.start()

All threads in a process share global variables, which is very convenient to share data among multiple threads. Disadvantages: threads change global variables randomly, which may cause confusion among multiple threads (that is, threads are not safe)

from threading import Thread
import time

def work1(nums):
    nums.append(44)
    print("----in work1---",nums)


def work2(nums):
    #Delay for a while to make sure things are done in thread t1
    time.sleep(1)
    print("----in work2---",nums)

g_nums = [11,22,33]

t1 = Thread(target=work1, args=(g_nums,))
t1.start()

t2 = Thread(target=work2, args=(g_nums,))
t2.start()

Mutex

When multiple threads modify a shared data almost at the same time, synchronization control is needed.
when a thread wants to change the shared data, lock it first. At this time, the resource status is “locked”, and other threads cannot change it; Until the thread releases the resource and changes its state to “non locked”, other threads can lock the resource again. Mutex ensures that only one thread can write each time, so as to ensure the correctness of data in the case of multithreading.

Advantages ensure that a piece of key code can only be completely executed by one thread from beginning to end. Disadvantages 1 prevent the concurrent execution of multiple threads. In fact, a piece of code containing locks can only be executed in single thread mode, which greatly reduces the efficiency. Disadvantages 2 because there can be multiple locks, different threads hold different locks, and try to obtain the lock held by the other party, Deadlock may occur

import threading
import time

g_num = 0

def test1(num):
    global g_num
    for i in range(num):
        mutex.acquire()  
        g_num += 1
        mutex.release()  

    print("---test1---g_num=%d"%g_num)

def test2(num):
    global g_num
    for i in range(num):
        mutex.acquire()  
        g_num += 1
        mutex.release()  

    print("---test2---g_num=%d"%g_num)

# Create a mutually exclusive lock
# Default is the unlocked state
mutex = threading.Lock()

# Create 2 threads and have them each add 1000000 times to g_num
p1 = threading.Thread(target=test1, args=(1000000,))
p1.start()

p2 = threading.Thread(target=test2, args=(1000000,))
p2.start()

# Wait for the calculation to complete
while len(threading.enumerate()) ! = 1:
    time.sleep(1)

print("The final result after 2 threads operate on the same global variable is:%s" % g_num)

Mutex can use context manager
to use lock, condition and semaphore in with statement
the object with acquire() and release() method provided by this module can be used as context manager of with statement. When entering a statement block, the acquire () method will be called, and when exiting a statement block, release () will be called. Therefore, the following fragments:

with some_lock:
    # do something...

# Equivalent to :

some_lock.acquire()
try:
    # do something...
finally:
    some_lock.release()

Lock, RLOCK, condition, semaphore, and boundedsemaphore objects can now be used as context managers for the with statement.

Multithreading case [chat]

Write a program with two threads
thread 1 is used to receive data and then display
thread 2 is used to detect keyboard data and then send data through UDP

import socket
import threading


def send_msg(udp_socket):
    """Get the keyboard data and send it to the other side """
    while True:
        # 1. input data from keyboard
        msg = input("\nPlease enter the data to be sent:")
        # 2. enter the ip address of the other party
        dest_ip = input("\nPlease enter the other party's ip address:")
        # 3. enter the other party's port
        dest_port = int(input("\nPlease enter the other party's port:"))
        # 4. send data
        udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))


def recv_msg(udp_socket):
    """Receive data and display """
    while True:
        # 1. receive data
        recv_msg = udp_socket.recvfrom(1024)
        # 2. decode
        recv_ip = recv_msg[1]
        recv_msg = recv_msg[0].decode("utf-8")
        # 3. display the received data
        print(">>>%s:%s" % (str(recv_ip), recv_msg))


def main():
    # 1. Create a socket
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 2. bind local information
    udp_socket.bind(("", 7890))

    # 3. create a child thread to receive data
    t = threading.Thread(target=recv_msg, args=(udp_socket,))
    t.start()
    # 4. let the main thread detect the keyboard data and send
    send_msg(udp_socket)


if __name__ == "__main__":
    main()

Read More: