\

A thread, sometimes called a lightweight process, is the smallest unit of a program’s execution flow. A standard thread consists of the thread ID, current instruction pointer (PC), register set, and stack. Thread is an entity in the process, which is the basic unit that is independently scheduled and dispatched by the system. Thread does not have private system resources, but it can share all resources owned by the process with other threads belonging to the same process. One thread can create and undo another thread, and multiple threads in the same process can execute concurrently.

A thread is a single sequence control flow in a program. Process has a relatively independent, schedulable execution unit, is the system independent scheduling and dispatching CPU basic unit instruction run program scheduling unit. The simultaneous running of multiple threads in a single program to accomplish different tasks is called multithreading. Python multithreading is used for I/O intensive tasks such as SocketServer network concurrency, and web crawlers.

Modern are multi-core processor, a few core processor can handle several threads at the same time, multi-threaded executable program looks at the same time, is actually a CPU to quickly switch between multiple threads to perform, among the upper and lower q switch is involved, a context switch is refers to a Thread Thread assigned ran out of time, The Thread information is stored, the CPU executes another Thread, and the CPU reads the Thread information and continues executing the Thread.

The threading module

The Python standard library provides two modules: _thread and threading. _thread provides a low-level, primitive thread, and a simple mutex, which is relatively limited compared to the threading module. The Threading module is an alternative to the _thread module. In most applications, the Threading module will continue to be used, so this book focuses on the use of the Threading module.

Python creates Thread objects using the following syntax:

import threading	
threading.Thread(target=None, name=None,  args=())
Copy the code

Main parameters:

  • Target is the name of the function that needs to be called.
  • Name Sets the name of the thread.
  • Parameters required by the arGS function are passed in as tuples
  • Thread object main method description:
  • Run (): A method used to indicate thread activity.
  • Start (): starts thread activity.
  • Join (): waits until the thread terminates.
  • IsAlive (): Returns whether the thread is active.
  • GetName (): Returns the thread name.
  • SetName (): Sets the thread name.

There are two ways to implement multithreading in Python: creating threads functionally and creating thread classes.

The first way to create a thread is:

When creating a Thread, you simply pass in a function that executes it and its arguments to complete the creation of the threading.thread instance. The following example uses the Thread class to generate two child threads, then start them and wait for them to finish,

Copy the code
  1. import threading

  2. import time,random,math

  3. \

  4. # idx number of cycles

  5. def printNum(idx):

  6. for num in range(idx ):

  7. Print the name of the currently running thread

  8. print("{0}\tnum={1}".format(threading.current_thread().getName(), num) )

  9. delay = math.ceil(random.random() * 2)

  10. time.sleep(delay)

  11. \

  12. if __name__ == '__main__':

  13. th1 = threading.Thread(target=printNum, args=(2,),name="thread1" )

  14. th2 = threading.Thread(target=printNum, args=(3,),name="thread2" )

  15. # start 2 threads

  16. th1.start()

  17. th2.start()

  18. Wait until the thread aborts

  19. th1.join()

  20. th2.join()

  21. Print ("{0} threadend ".format(threading.current_thread().getName()))

Running the script yields the following results.

Copy the code
  1. thread1 num=0
  2. thread2 num=0
  3. thread1 num=1
  4. thread2 num=1
  5. thread2 num=2
  6. MainThread The thread ends. Procedure

Running a script starts a thread by default, which is called the main thread. The main thread has the ability to start new threads. Python’s threading module has a current_thread() function that returns an example of the current thread. From the example of the current thread, you can get the name of the previous running thread, with the core code below.

Copy the code
  1. threading.current_thread().getName()

To start a Thread, you pass in a function and arguments, create a Thread instance, and call start() to start execution

Copy the code
  1. th1 = threading.Thread(target=printNum, args=(2,),name="thread1" )
  2. th1.start()

You can see from the result that the name of the MainThread sample is MainThread, and the name of the child thread is specified at creation time. This example creates two child threads named thread1 and thread2. If no Thread is given a name, Python automatically names the Thread thread-1, thread-2… And so on. In this example, the thread function printNum() is defined to print idX records and exit, each time using time.sleep() to put the program to sleep for a period of time.

The second way to create a thread is to create a thread class

Create a Thread object directly from a subclass of threading.Thread. By inheriting Thread and rewriting the Run () method of Thread, the specific tasks to be performed are defined in the run() method. In the Thread class, a start() method is provided to start a new process, and the run() method is automatically called when the Thread is started.

Copy the code
  1. import threading

  2. import time,random,math

  3. \

  4. class MutliThread(threading.Thread):

  5. \

  6. def __init__(self, threadName,num):

  7. threading.Thread.__init__(self)

  8. self.name = threadName

  9. self.num = num

  10. \

  11. def run(self):

  12. for i in range(self.num):

  13. print("{0} i={1}".format(threading.current_thread().getName(), i))

  14. delay = math.ceil(random.random() * 2)

  15. time.sleep(delay)

  16. \

  17. if __name__ == '__main__':

  18. thr1 = MutliThread("thread1",3)

  19. thr2 = MutliThread("thread2",2)

  20. # start thread

  21. thr1.start()

  22. thr2.start()

  23. Wait until the thread aborts

  24. thr1.join()

  25. thr2.join()

  26. Print ("{0} threadend ".format(threading.current_thread().getName()))

Running the script yields the following results.

Copy the code
  1. thread1 i=0
  2. thread2 i=0
  3. thread1 i=1
  4. thread2 i=1
  5. thread1 i=2
  6. MainThread The thread ends. Procedure

Create thr1 and thr2, override the run() function of Thread, put business logic in it, and start the Thread by calling the start() method of Thread object. Continue by calling the join() function on the thread object and waiting for the thread to complete.

In this case, the MainThread waits for the child thread1 and thread2 threads to finish before printing “MainThread terminated”. If thread1 and thread2 do not call join(), then the MainThread and the two children execute in parallel. When the two children add join(), the program executes sequentially. Therefore, when a child thread uses a join(), the main thread usually waits until the other child threads have completed their execution. The other child threads do not need to wait for each other.

Daemon thread

In thread modules, join() is used with child thread objects, and the main thread relies on the child thread to complete its execution before continuing to execute the code. If the child thread does not use join(), the main thread and the child thread run in parallel, without dependencies. The main thread executes, and the child thread executes.

In multithreaded development, if the child thread is set up as a daemon thread, the daemon thread will wait for the main thread to finish running and then be destroyed. A main thread can have multiple daemons. Daemons can run only if the main thread exists. If the main thread does not exist, the daemons will be destroyed.

In this example, create 1 main thread and 3 child threads, let the main thread and child threads execute in parallel. It reads as follows.

Copy the code
  1. import threading, time

  2. \

  3. def run(taskName):

  4. Print (" task :", taskName)

  5. time.sleep(2)

  6. Print ("{0} task completed ". Format (taskName)) # print("{0} task completed"

  7. \

  8. if __name__ == '__main__':

  9. start_time = time.time()

  10. for i in range(3):

  11. thr = threading.Thread(target=run, args=("task-{0}".format(i),))

  12. Set child thread to daemon thread

  13. thr.setDaemon(True)

  14. thr.start()

  15. \

  16. # check the main thread and the number of active threads

  17. Format (threading.current_thread().getName(), threading.active_count())) print(" threading.current_thread(), threading.active_count()) ")

  18. Print (" time :", time.time() -start_time)

  19. .

Running the script yields the following results:

Copy the code
  1. Tasks: task - 0
  2. Tasks: task 1
  3. Tasks: task - 2
  4. MainThread Ends when the number of threads is 4
  5. Consumption time: 0.0009751319885253906
  6. Task-2 The task is complete
  7. Task-0 The task is complete
  8. Task-1 The task is complete

As you can see from the return result, the current number of threads is 4, the number of threads = the number of main threads + the number of child threads, in this case there is 1 main thread and 3 child threads. After the main thread completes execution, the program will exit until the child thread completes execution.

Based on this example, all child threads are set as daemons. After the child thread becomes a daemon thread, the program exits as soon as the main thread completes execution, regardless of whether the thread completes execution. Use the setDaemon(True) function of the thread object to set up the daemon thread.

Copy the code
  1. import threading, time

  2. \

  3. def run(taskName):

  4. Print (" task :", taskName)

  5. time.sleep(2)

  6. Print ("{0} task completed ". Format (taskName))

  7. \

  8. if __name__ == '__main__':

  9. start_time = time.time()

  10. for i in range(3):

  11. thr = threading.Thread(target=run, args=("task-{0}".format(i),))

  12. Set child thread as daemon thread before starting thread

  13. thr.setDaemon(True)

  14. thr.start()

  15. \

  16. # check the main thread and the number of active threads

  17. thrName = threading.current_thread().getName()

  18. thrCount = threading.active_count()

  19. Print ("{0} thread end, thrCount ={1}". Format (thrName, thrCount)

  20. Print (" time :", time.time() -start_time)

Running the script yields the following results.

Copy the code
  1. Tasks: task - 0
  2. Tasks: task 1
  3. Tasks: task - 2
  4. MainThread Ends when the number of threads is 4
  5. Elapsed time: 0.0010023117065429688

As you can see from the results of this example, after the main thread completes execution, the program does not wait for the daemon thread to complete execution and exit. To set a thread object to a daemon, be sure to set it before the thread object calls start().

Multi-threaded locking mechanism

Multithreaded programming has problems accessing shared variables, but multiprocess programming does not. In a multi-process, a copy of the same variable exists in each process and does not affect each other, whereas in multi-threading, all variables are shared by all threads.Copy the code

Multiple processes do not conflict with variables in memory. A process consists of multiple threads, which may affect the sharing of variables in memory. Therefore, deadlocks occur.

1. Scope of variables

Variables defined outside a function are called global variables, and variables defined inside a function are called local variables. Global variables are readable in all scopes. Local variables are readable only in this function. When a function reads a variable, it reads its own local variable first, and then reads the global variable. It reads as follows.

Copy the code
  1. # global variables

  2. balance = 1

  3. \

  4. def change():

  5. Define global variables

  6. global balance

  7. balance = 100

  8. # define local variables

  9. num = 20

  10. print("change() balance={0}".format(balance) )

  11. \

  12. if __name__ == "__main__" :

  13. change()

  14. Print (" change balance={0}". Format (balance))

Running the script yields the following results.

Copy the code
  1. change() balance=100
  2. The new balance is 100

If you comment out global in the change() function

Copy the code
  1. V1, so you get a return value of 1.
  2. change() balance=100
  3. The new balance is 1

In this example, the variable balance defined outside change() is a global variable, and the variable num defined inside change() is a local variable. The global variable is readable by default and can be used in any function. If you need to change the value of the global variable, you need to define the global variable inside the function using global. In this example, global is used inside the change() function to define the global variable balance, which can be changed inside the function.

You can use a global variable in a function, but you cannot change a global variable in a function. If you want multiple threads to share variables, you need to use global variables. Add the global keyword global to the method to define global variables that can be modified by multiple threads to share variables.

2. Locks in multiple threads

Data security problems occur when multi-threads modify global variables at the same time. Thread insecurity means that data access protection is not provided. Multiple threads may change data successively, resulting in dirty data. In this example, when we generate two threads simultaneously to modify the global variable balance in the change() function, data inconsistencies occur.

The case file is named PythonFullStack\Chapter03\threadDemo03.py and contains the following contents.

Copy the code
  1. import threading

  2. \

  3. balance = 100

  4. \

  5. def change(num, counter):

  6. global balance

  7. for i in range(counter):

  8. balance += num

  9. balance -= num

  10. if balance ! = 100:

  11. # if this statement is printed, the thread is not safe

  12. print("balance=%d" % balance)

  13. break

  14. \

  15. if __name__ == "__main__":

  16. Thr1 = threading. Thread (target = change, args = (100500 000), name = 't1')

  17. Thr2 = threading. Thread (target = change, args = (100500 000), name = 't2')

  18. thr1.start()

  19. thr2.start()

  20. thr1.join()

  21. thr2.join()

  22. Print ("{0} threadend ".format(threading.current_thread().getName()))

Running the script above, the following results occur when two threads run 500,000 times.

Copy the code
  1. balance=200
  2. MainThread The thread ends. Procedure

In this example, we define a global variable balance with an initial value of 100. When two threads are started, the value is added and then subtracted. In theory, balance should be 100. The scheduling of threads is determined by the operating system. When threads T1 and T2 alternate, the balance result may not be 100 as long as there are enough loops. As can be seen from the results, in this example, data inconsistency occurs when threads T1 and T2 modify the global variable balance simultaneously.

Pay attention to

In multithreading, all global variables are shared by all threads. Therefore, any variable can be modified by any thread, so the biggest danger of sharing data between threads is that multiple threads change a variable at the same time, making the contents of the variable messy.

In multithreaded situations, using global variables does not share data, causing thread-safety issues. Thread safety refers to the locking mechanism used in multi-thread access. When one thread accesses a certain data of the class, it is protected and cannot be accessed by other threads until the thread finishes reading the data. There will be no data inconsistencies

There are no code safety issues when running in a single thread. When writing multithreaded programs, generating one thread does not mean multithreading. It is in the case of multi-threading that security issues arise.

For thread-safety issues, “mutex” is needed, just as for manipulating data in a database, locks are also needed. When a thread wants to change the shared data, it locks it first. In this case, the resource status is locked, and other threads cannot change it. Other threads cannot lock the resource again until the thread releases the resource, making its state “unlocked.” The mutex ensures that only one thread writes at a time, thus ensuring data correctness in multithreaded situations.

The core code for a mutex is as follows:

Copy the code
  1. # to create lock

  2. mutex = threading.Lock()

  3. \

  4. # lock

  5. mutex.acquire()

  6. \

  7. # release

  8. mutex.release()

To ensure the balance calculation is correct, use threading.lock () to create the Lock object Lock and add lock.acquire() and lock.release() to the synchronization block, which in this case is the global variable balance.

When a thread executes change() and obtains a lock through lock.acquire(), the other thread cannot execute a synchronized block and must wait until the lock is released before it can execute a synchronized block. Because there is only one lock, and no matter how many threads hold the lock at any one time, changing the global variable balance does not cause conflicts. The improved code looks like this.

Copy the code
  1. import threading

  2. \

  3. balance = 100

  4. lock = threading.Lock()

  5. \

  6. def change(num, counter):

  7. global balance

  8. for i in range(counter):

  9. Get the lock first

  10. lock.acquire()

  11. balance += num

  12. balance -= num

  13. # releases the lock

  14. lock.release()

  15. \

  16. if balance ! = 100:

  17. # if this statement is printed, the thread is not safe

  18. print("balance=%d" % balance)

  19. break

  20. \

  21. if __name__ == "__main__":

  22. Thr1 = threading. Thread (target = change, args = (100500 000), name = 't1')

  23. Thr2 = threading. Thread (target = change, args = (100500 000), name = 't2')

  24. thr1.start()

  25. thr2.start()

  26. thr1.join()

  27. thr2.join()

  28. Print ("{0} threadend ".format(threading.current_thread().getName()))

In this example, when two threads run Lock.acquire () at the same time, only one thread succeeds in acquiring the lock and then executes the code, while the other threads continue to wait until they acquire the lock location. The thread that acquired the lock must release the lock when it is finished, otherwise other threads will wait forever and become dead.

Running the script produces no output, proving that the code is safe. Add Lock.acquire () and Lock.release () to the synchronization block, and be careful not to add too much lock force. The second thread cannot run until the first thread runs, so the lock must be added to the code that needs to be synchronized.

We will send a free copy of “Python 3.x Full Stack Development from Beginner to Master” published by Peking University Press. The whole process of Python stack development is explained in a “disassembly” way. This book integrates theory, technology, cases, and project development experience into one, and shows the key points, doubts, and difficulties in the development process through a large number of examples. It is a treasure book. Jingdong mid-year shopping festival, every 100 reduced 50.

Hot recommended in Python create WeChat robot in Python robot to monitor WeChat group chat in Python for cameras and real-time control face open source project | beautification LeetCode warehouse is recommended in Python Python Chinese community’s several kind of public service, announced notice | Python Chinese Community award for articles \