一,进程与线程
什么是线程(thread)?
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程之中的实际运作单位,一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
A thread is an execution context, which is all the information a CPU needs to execute a stream of instructions.
Suppose you’re reading a book, and you want to take a break right now, but you want to be able to come back and resume reading from the exact point where you stopped. One way to achieve that is by jotting down the page number, line number, and word number. So your execution context for reading a book is these 3 numbers.
If you have a roommate, and she’s using the same technique, she can take the book while you’re not using it, and resume reading from where she stopped. Then you can take it back, and resume it from where you were.
Threads work in the same way. A CPU is giving you the illusion that it’s doing multiple computations at the same time. It does that by spending a bit of time on each computation. It can do that because it has an execution context for each computation. Just like you can share a book with your friend, many tasks can share a CPU.
On a more technical level, an execution context (therefore a thread) consists of the values of the CPU’s registers.
Last: threads are different from processes. A thread is a context of execution, while a process is a bunch of resources associated with a computation. A process can have one or many threads.
Clarification: the resources associated with a process include memory pages (all the threads in a process have the same view of the memory), file descriptors (e.g., open sockets), and security credentials (e.g., the ID of the user who started the process).
什么又是进程呢?
比如说,QQ或WeChat就可以称之为一个独立的进程。QQ要以一个整体的形式暴露给操作系统去管理,里面包含了对各种资源的调用,内存的管理,网络接口的调用等…对各种资源管理的集合,就可以称为进程。
An executing instance of a program is called a process.
Each process provides the resources needed to execute a program. A process has a virtual address space, executable code, open handles to system objects, a security context, a unique process identifier, environment variables, a priority class, minimum and maximum working set sizes, and at least one thread of execution. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads.
那进程与线程有什么区别呢?
1,Threads share the address space of the process that created it; processes have their own address space.
线程共享内存空间,进程的内存是独立的
2,Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.
线程可以直接访问进程的数据段;进程拥有自己的父进程的数据段副本。
3,Threads can directly communicate with other threads of its process; processes must use inter process communication to communicate with sibling processes.
同一个进程的线程之间可以直接通信;两个进程要想通信必须通过一个中间代理来实现。
4,New threads are easily created; new processes require duplication of the parent process.
新的线程容易创建, 新的进程需要对其父进程进行一次克隆。
5,Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.
一个线程可以控制操作同一进程里的其他线程;但是进程只能操作子进程。
6,Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.
主线程的改变(取消、优先级改变等)可能会影响进程中其他线程的行为;对父进程的更改不会影响子进程。
哈哈,要想理解的更彻底,还得去看原英文的解释!!!
下面来看简单的多线程例子
# 多线程
import threading
def run(n):
print("task:", n)
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
# 传统写法
def run(n):
print("task:", n)
run("t1")
run("t2")
结果为
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading.py
task: t1
task: t2
Process finished with exit code 0
但是当你执行时发现都是瞬间就打印出结果了,没感觉多线程的要快呀?哎,客官稍安勿躁,咱们略微修改下
# 多线程并发
import threading
import time
def run(n):
print("task:", n)
time.sleep(2)
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
结果为:
import time
def run(n):
print("task:", n)
time.sleep(2)
run("t1")
run("t2")
结果为:
嘻嘻,这下感受到多线程的优势了吧!
多线程还有另一种写法,就是继承式调用
# 继承式调用
import threading
class MyThread(threading.Thread):
def __init__(self, n):
super(MyThread, self).__init__()
self.N = n
def run(self):
print("running task,", self.N)
t1 = MyThread("my name is hepengli!")
t2 = MyThread("you name is liming!")
t1.start()
t2.start()
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading_2.py
running task, my name is hepengli!
running task, you name is liming!
Process finished with exit code 0
假如要启动更多的线程,就可以用for循环
import threading
def run(n):
print("task:", n)
for i in range(10):
t1 = threading.Thread(target=run, args=("t>>%s" % i,))
t1.start()
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading_3.py
task: t>>0
task: t>>1
task: t>>2
task: t>>3
task: t>>4
task: t>>5
task: t>>6
task: t>>7
task: t>>8
task: t>>9
Process finished with exit code 0
如果想计算下所用时间
import threading
import time
start_time = time.time()
def run(n):
print("task:", n)
time.sleep(2)
for i in range(10):
t1 = threading.Thread(target=run, args=("t>>%s" % i,))
t1.start()
print("cost all time is: \033[31;1m%s\033[0m" % (time.time() - start_time))
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading_3.py
task: t>>0
task: t>>1
task: t>>2
task: t>>3
task: t>>4
task: t>>5
task: t>>6
task: t>>7
task: t>>8
task: t>>9
cost all time is: 0.002992868423461914
Process finished with exit code 0
有没有发现一个问题,不应该时间大于2s吗?怎么计算的时间这么短?
其实呀,这就是多线程,主线程执行完毕并没有等其他的子线程执行完就结束了。也就是说,一个程序至少有一个线程,当主线程启动子线程之后,子线程就是独立的了,so说他们是并行的。即这样不能测出来总共所花的时间,那怎么办呢?Keep looking down!
import threading
import time
def run(n):
print("task:", n)
time.sleep(2)
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t1.join() # 等待第一个线程执行完毕再执行第二个线程,相当于串行
t2.start()
结果为:
import time
import threading
def run(n):
print("task:", n, threading.current_thread(), threading.active_count()) # 当前的线程/当前的线程个数
time.sleep(2)
start_time = time.time()
t_objs = [] # 存线程实例
for i in range(10):
t = threading.Thread(target=run, args=("t>>:%s" % i,))
t.start()
t_objs.append(t)
for t in t_objs:
t.join()
print(">>>>>", threading.current_thread(), threading.active_count()) # 当前的线程/当前的线程个数
print("Time spent is ", time.time() - start_time)
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading_3.py
task: t>>:0 <Thread(Thread-1, started 2288)> 2
task: t>>:1 <Thread(Thread-2, started 13892)> 3
task: t>>:2 <Thread(Thread-3, started 15316)> 4
task: t>>:3 <Thread(Thread-4, started 14328)> 5
task: t>>:4 <Thread(Thread-5, started 3680)> 6
task: t>>:5 <Thread(Thread-6, started 6676)> 7
task: t>>:6 <Thread(Thread-7, started 2692)> 8
task: t>>:7 <Thread(Thread-8, started 14628)> 9
task: t>>:8 <Thread(Thread-9, started 13864)> 10
task: t>>:9 <Thread(Thread-10, started 3724)> 11
>>>>> <_MainThread(MainThread, started 9944)> 1
Time spent is 2.004971981048584
Process finished with exit code 0
仔细看里面的主线程和子线程
嘻嘻,是不是效果达到了!奈斯
守护进程
无论是进程还是线程,都遵循:守护进程/线程会等待主进程/线程运行完毕后被销毁。相当于仆人与主人之间的关系,守护进程/线程就是仆人。
import time
import threading
def run(n):
print("task:", n, threading.current_thread(), threading.active_count()) # 当前的线程/当前的线程个数
time.sleep(2)
start_time = time.time()
for i in range(10):
t = threading.Thread(target=run, args=("t>>:%s" % i,))
t.setDaemon(True) # 把当前线程设置为守护线程
t.start()
time.sleep(2)
print(">>>>>", threading.current_thread(), threading.active_count()) # 当前的线程/当前的线程个数
print("Time spent is ", time.time() - start_time)
G:\Python38\python.exe G:/Project1/self_taught/week_9/more_threading_3.py
task: t>>:0 <Thread(Thread-1, started daemon 8564)> 2
task: t>>:1 <Thread(Thread-2, started daemon 5744)> 3
task: t>>:2 <Thread(Thread-3, started daemon 276)> 4
task: t>>:3 <Thread(Thread-4, started daemon 10388)> 5
task: t>>:4 <Thread(Thread-5, started daemon 10776)> 6
task: t>>:5 <Thread(Thread-6, started daemon 12048)> 7
task: t>>:6 <Thread(Thread-7, started daemon 13056)> 8
task: t>>:7 <Thread(Thread-8, started daemon 8852)> 9
task: t>>:8 <Thread(Thread-9, started daemon 14440)> 10
task: t>>:9 <Thread(Thread-10, started daemon 6972)> 11
>>>>> <_MainThread(MainThread, started 10004)> 4
Time spent is 2.0123820304870605
Process finished with exit code 0
信号量(Semaphore)
Semaphore是同时允许一定数量的线程更改数据,,比如说,厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面的人出来再进去。
# 信号量
import threading
import time
def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread:%s" % n)
semaphore.release()
if __name__ == "__main__":
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass
else:
print("all threads done!")
G:\Python38\python.exe G:/Project1/self_taught/week_9/Semaphore_file.py
run the thread:0run the thread:1
run the thread:2run the thread:3
run the thread:4
run the thread:9run the thread:6
run the thread:5
run the thread:7
run the thread:8
all threads done!
Process finished with exit code 0
Timer
This class represents an action that should be run only after a certain amount of time has passed
Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling thecancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user.
import threading
def hello():
print("hello, world!")
t = threading.Timer(5, hello) # 计时器
t.start()
G:\Python38\python.exe G:/Project1/self_taught/week_9/paramiko_pra.py
hello, world!
Process finished with exit code 0
Events
An event is a simple synchronization object;
the event represents an internal flag, and threads
can wait for the flag to be set, or set or clear the flag themselves.
事件是一个简单的同步对象;
事件表示内部标志和线程
可以等待设置标志,也可以自行设置或清除标志。
event = threading.Event()
下面看一个简单的red_green_light
import time
import threading
event = threading.Event()
def light():
count = 1
event.set() # 现设绿灯
while True:
if 5 < count <= 10: # 改成红灯
event.clear() # 把标志位清空
print("\033[41;1mRed light is on...\033[0m")
elif count > 10:
event.set() # 再变绿灯
count = 1
else:
print("\033[42;1mGreen is on...\033[0m")
time.sleep(1)
count += 1
def car(name):
while True:
if event.is_set(): # 代表绿灯
print("[%s] running..." % name)
time.sleep(1)
else:
print("[%s] red light...waiting..." % name)
event.wait()
print("\033[34;1m[%s]Green light is on,start go..." % name)
light1 = threading.Thread(target=light,)
light1.start()
car1 = threading.Thread(target=car, args=("Tesla",))
car1.start()
队列
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
当必须在多个线程之间安全地交换信息时,队列在线程编程中特别有用。
# 队列
import queue
q = queue.Queue() # 先入先出
q.put("d1") # 放入
q.put("d2") # 放入
q.put(1)
q.put(2)
print(q.qsize()) # 大小
print(q.get()) # 取出
print(q.get()) # 取出
print(q.qsize())
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
4
d1
d2
2
Process finished with exit code 0
设置最大放入量
import queue
q = queue.Queue(maxsize=3) # 设置容量为3
q.put("d1") # 放入
q.put("d2") # 放入
q.put(1)
q.put(2)
print(q.qsize()) # 大小
print(q.get()) # 取出
print(q.get()) # 取出
print(q.get()) # 取出
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
结果直接卡死,需要手动停止。
假如只放入了两个,但是却取了三次会怎样?
# 队列
import queue
q = queue.Queue() # 先入先出
q.put("d1") # 放入
q.put("d2") # 放入
print(q.get()) # 取出
print(q.get()) # 取出
print(q.get()) # 取出
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
d1
d2
对,程序取第三个时直接卡死。。。这个有啥解决办法没?当然有
# 队列
import queue
q = queue.Queue() # 先入先出
q.put("d1") # 放入
q.put("d2") # 放入
print(q.get()) # 取出
print(q.get()) # 取出
print(q.get_nowait()) # 取不出来就抛出异常
# print(q.get(block=False)) # 和上一个相同
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
d1
d2
Traceback (most recent call last):
File "G:/Project1/self_taught/week_9/queue_prc.py", line 15, in <module>
print(q.get_nowait())
File "G:\Python38\lib\queue.py", line 198, in get_nowait
return self.get(block=False)
File "G:\Python38\lib\queue.py", line 167, in get
raise Empty
_queue.Empty
Process finished with exit code 1
当然可以用抓住异常的办法,避免卡死或出错造成不必要的小尴尬!!!
上面是先放入先取出,再看后放入先取出的
import queue
q = queue.LifoQueue() # 后入先出
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
3
2
1
Process finished with exit code 0
再看开“vip”的
import queue
q = queue.PriorityQueue() # 设置优先级
q.put((4, "hepengli"))
q.put((3, "hauhua"))
q.put((10, "huanhuan"))
q.put((-2, "xiaoming"))
print(q.get())
print(q.get())
print(q.get())
print(q.get())
G:\Python38\python.exe G:/Project1/self_taught/week_9/queue_prc.py
(-2, 'xiaoming')
(3, 'hauhua')
(4, 'hepengli')
(10, 'huanhuan')
Process finished with exit code 0
就是前面的那个数字越小越先取出来
生产者消费者模型
import threading
import time
import queue
q = queue.Queue(maxsize=10)
def producer(name):
count = 1
while True:
q.put("bone%s" % count)
print("\033[32;1m%s bones were produced\033[0m" % count)
count += 1
time.sleep(0.1)
def consumer(name):
# while q.qsize() > 0:
while True:
print("\033[31;1m[%s] get [%s] and eat it...\033[0m" % (name, q.get()))
time.sleep(1)
p1 = threading.Thread(target=producer, args=("hepengli",))
p2 = threading.Thread(target=consumer, args=("dog",))
p3 = threading.Thread(target=consumer, args=("tiger",))
p1.start()
p2.start()
p3.start()
也可以尝试更改等待时间,会发生更有意思的事(供大于求或供小于求)。
好了,这周的学习内容就到此为止,望各位看客大佬发现有不足或错误能留言相告,臣不胜感激!!!