python 并发

线程

1、一个应用程序,可以有多进程和多线程

2、python默认为:单进程,单线程

3、单进程,多线程使用场景:IO操作,不占用cpu,

  多进程使用场景:计算型,大量占用cpu

4、GIL,全局解释器锁

 

thread 模块提供的其他方法:
threading.currentThread(): 返回当前的线程变量
threading.enumerate(): 返回一个包含正在运行的线程的list,正在运行指线程启动后、结束前,不包括启动前和终止后的线程
threading.active_count(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
threading.current_thread(): 返回当前线程对象
 
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
    name是线程名称。默认情况下,一个唯一的名称是由形式”thread-n“N是一个小的十进制数
    daemon默认为前台执行,True为后台执行
    start    线程准备就绪,等待CPU调度
    setName   为线程设置名称
    getName   获取线程名称
    setDaemon  设置为后台线程或前台线程(默认)如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止。如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
    join     逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义,编程串行执行
    run      线程被cpu调度后自动执行线程对象的run方法
 
简单线程
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...")
end...
thrad0
thrad1
thrad4
thrad3
thrad2
 
线程锁
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()Processstart()启动某个进程。 

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.

  多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

  在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁,但是局部变量也有问题,就是在函数调用的时候,传递起来很麻烦

posted on 2016-07-21 23:29  逸秋  阅读(47)  评论(0)    收藏  举报

导航