python 并发
线程
1、一个应用程序,可以有多进程和多线程
2、python默认为:单进程,单线程
3、单进程,多线程使用场景:IO操作,不占用cpu,
多进程使用场景:计算型,大量占用cpu
4、GIL,全局解释器锁
import threading
import time
def show(arg):
time.sleep(1)
print("thrad" + str(arg))
for i in range(5):
t = threading.Thread(target=show, args=(i,))
t.start()
print("end...")
import time, threading
balance = 0
lock = threading.Lock()
def change_it(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
lock.acquire() #申请锁
change_it(n) #对全局数据进行操作
lock.release() #释放锁
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
0
信号量
import threading
import time
NUM = 10
def func(lock):
global NUM
lock.acquire()
NUM -= 1
time.sleep(2)
print(NUM)
lock.release()
lock = threading.BoundedSemaphore(5) #信号量,同时释放5个线程
for i in range(10):
t = threading.Thread(target=func, args=(lock,))
t.start()
event
import threading
def func(i, event):
print(i)
event.wait() #检测事件是什么状态,如果是红灯,在事件暂停,如果是绿灯,则事件继续,等待event.set()事件发生
print(i+100)
event = threading.Event()
for i in range(5):
t = threading.Thread(target=func, args=(i, event,))
t.start()
event.clear() #设置成红灯,默认事件为红灯
inp = input(">>>")
if inp == "1":
event.set() #设置成绿灯,否则事件一值处于等待
条件执行Condition
import threading
def run(n):
con.acquire()
con.wait()
print("run the thread: %s" % n)
con.release()
if __name__ == '__main__':
con = threading.Condition()
for i in range(10):
t = threading.Thread(target=run, args=(i,))
t.start()
while True:
inp = input('>>>')
if inp == 'q':
break
con.acquire() #这3个必须一起使用
con.notify(int(inp)) #每次释放几个线程,在run函数中的wait处线程释放几个
con.release()
线程池
import queue
import threading
import contextlib
import time
STOP = "stop event"
class ThreadPool:
current_thread = threading.currentThread
def __init__(self, thread_num):
self.q = queue.Queue()
self.cancel = False
self.terminal = False
self.thread_list = []
self.__init_thread(thread_num)
def __init_thread(self, num):
for _ in range(num):
self.thread_list.append(ThreadPool.current_thread)
@contextlib.contextmanager
def work_state(self, thread_list, worker_thread):
thread_list.append(worker_thread)
try:
yield
finally:
thread_list.remove(worker_thread)
def call(self):
event = self.q.get()
while event != STOP:
func, args, callback = event
try:
result = func(*args)
success = True
except:
result = None
success = False
if callback is not None:
try:
callback(success, result)
except:
pass
with self.work_state(self.thread_list, ThreadPool.current_thread):
if self.terminal:
event = STOP
else:
event = self.q.get()
else:
self.thread_list.remove(ThreadPool.current_thread)
def close_thread(self):
self.cancel = True
full_size = len(self.thread_list)
while full_size:
self.q.put(STOP)
full_size -= 1
def terminate(self):
self.terminal = True
while self.thread_list:
self.q.put(STOP)
self.q.empty()
def put(self, func, args, callback=None):
w = (func, args, callback)
self.q.put(w)
def run(self):
if self.cancel:
return
for j in range(len(self.thread_list)):
t = threading.Thread(target=self.call)
t.start()
if __name__ == "__main__":
pool = ThreadPool(5)
pool.run()
def f1(arg):
print("current_thread", threading.current_thread()) #输出当前线程对象
time.sleep(2)
return arg
def callback(status, result):
if status:
print("call back f1 arg:", result)
else:
print("执行失败...")
while True:
n = input("线程并发数量:")
if not n:
continue
elif n == "q":
break
else:
for i in range(int(n)):
pool.put(f1, (i,), callback)
pool.close_thread()
进程
multiprocessing.Process([target=函数名], [ name = 别名], [args=(参数,)], [kwargs=调用对象的字典,])
daemon:设置子进程为后台进程,默认为前台进程(False) ,父进程终止后自动终止,且自己不能产生新进程,必须在start()之前设置
pid: 获取进程的ID号
name:获取进程的别名
exitcode:进程在运行时为None、如果为–N,表示被信号N结束
multiprocessing.Process可使用的方法:
is_alive():判断当前进程是否为活动状态
join([timeout]):和多线程一样,等待其它子进程结束后主进程才继续执行
run():调用start()方法后自动调用run()方法
start():Process以start()启动某个进程。
terminate():结束进程方法
from multiprocessing import Process
import os
def run_proc(name):
print("run child process %s (%s)"%(name,os.getpid())) #取出进程的名称,以及进程的pid
if __name__=="__main__":
print('Parent process %s'%os.getpid()) #显示当前进程的pid
p=Process(target=run_proc,args=('test process',)) #创建一个子进程
p.start() #启动子进程
print("process start pid: %s, name:%s, alive:%s"%(p.pid,p.name,p.is_alive())) #pid,name存活
p.join() #等待子进程结束后继续运行当前进程
print('process end')
Parent process 9579
process start pid: 9580, name:Process-1, alive:True
run child process test process (9580)
process end
进程池
from multiprocessing import Pool
import os,time,random
def long_time_task(name):
print('run task subprocess %s (%s)..'%(name,os.getpid()))
start_time=time.time()
time.sleep(random.random()*3)
end_time=time.time()
print('task %s runs %0.2f seconds..'%(name,(end_time-start_time)))
if __name__=='__main__':
print('Parent process %s'%os.getpid())
p=Pool()
print('waiting for all subprocess done...')
for i in range(5):
p.apply_async(long_time_task,args=(i,))
p.close()
p.join()
print('all subprocess done..')
Parent process 9704
waiting for all subprocess done...
run task subprocess 0 (9705)..
run task subprocess 1 (9706)..
run task subprocess 2 (9707)..
run task subprocess 3 (9708)..
task 1 runs 0.19 seconds..
run task subprocess 4 (9706)..
task 0 runs 0.79 seconds..
task 3 runs 0.92 seconds..
task 2 runs 1.42 seconds..
task 4 runs 2.27 seconds..
all subprocess done..
进程间共享数据
from multiprocessing import Process,queues
import os,time,random
def write(q): #写数据函数
for value in ['A','B','C']:
print('Put %s to queye..'%value)
q.put(value)
time.sleep(random.random())
def read(q): #读取数据函数
while True:
value=q.get(True)
print('Get %s from queue.'%value)
if __name__=='__main__':
q=queues.Queue() #父进程创建Queue,并传给各个子进程
pw=Process(target=write,args=(q,))
pr=Process(target=read,args=(q,))
pw.start() #启动子进程pw,进行写入
pr.start() #启动子进程pr,进行读取
pw.join() #等待子进程pw,结束,join中可以时间等待时长
pr.terminate() #读取子进程是死循环,需要强制结束
Put A to queye..
Get A from queue.
Put B to queye..
Get B from queue.
Put C to queye..
Get C from queue.
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁,但是局部变量也有问题,就是在函数调用的时候,传递起来很麻烦
浙公网安备 33010602011771号