线程是CPU使用的基本单元,由主线程来创建,并使用这个进程的资源。python中thread模块提供支持,通过以下两种方法创建线程:
通过threading.Thread直接在线程中运行函数通过继承threading.Thread类来创建线程这里我们就用这个函数创建一个线程,简单模拟打印平方,这个函数原型我们首先熟悉一下,尤其是各个参数
Thread(group=None, target=None, Name=None, args=(), kwargs={}, *, daemon = None) # 其中target参数就是运行的函数,args是传入函数的参数元组。运行效果:(并行效果哟!)
import threading def thrfun(x, y): for i in range(x, y): print(str(i * i) + ';') ta = threading.Thread(target=thrfun, args=(1, 6)) tb = threading.Thread(target=thrfun, args=(16, 21)) tc = threading.Thread(target=thrfun, args=(11,15)) ta.start() tb.start() tc.start()博主用pycharm是同步执行,但是代码本身含义是并行。因此大家如果使用pycharm同步的话,可以用ipython试试。
这个创建类来继承就行了,了解面向对象的同学应该明白,肯定有init的方法,我们把要执行的函数写到run函数里,因为它继承可以重载。我们还是仍以求平方为例子
实验效果: 实验代码:
import threading class myThread(threading.Thread): def __init__(self,mynum): super().__init__() self.mynum = mynum def run(self): for i in range(self.mynum,self.mynum+5): print(str(i*i)+';') ma = myThread(1) mb = myThread(16) ma.start() mb.start()上面的例子我们只是用thread里的init和run方法,还有几个重要的方法和属性一起熟悉一下: 方法:
join([timeout])isAlive()属性:
namedaemon # join()方法的作用是当某个线程或函数执行时需等另一个线程 # --完成后才能继续,则应调用另一个线程的join()方法 # 其中可选参数timeout用于指定线程运行的最长时间 # isAlive()方法用于查看线程是否运行 # 属性 # name属性是线程设置的线程名 # daemon属性用来设置线程是否随主线程退出而退出,一般来说,其属性值为True不会随主线程退出而退出下面就以两个例子演示join和daemon的使用
实验效果: 实验代码:
import threading import time def thrfun(x, y, thr=None): if thr: thr.join() else: time.sleep(2) for i in range(x, y): print(str(i * i) + ';') ta = threading.Thread(target=thrfun, args=(1, 6)) tb = threading.Thread(target=thrfun, args=(16, 21,ta)) ta.start() tb.start()这里的join函数本传入了ta,也就是tb线程应等待ta结束后运行。
实验效果: 实验代码:
import threading import time class myThread(threading.Thread): def __init__(self,mynum): super().__init__() self.mynum = mynum def run(self): time.sleep(1) for i in range(self.mynum, self.mynum + 5): print(str(i * i) + ';') def main(): print('start...') ma = myThread(1) mb = myThread(16) ma.daemon =True mb.daemon = True ma.start() mb.start() print('end...') if __name__ == '__main__': main()将打印平方的工作,都交到后台去了,这里不做执行
线程的隔离性就不需要彼此修改,因此产生锁的概念。python中threading模块中的对象Lock和RLock进行简单的同步,对于同一个时刻只允许一个线程操作的数据对象,可以进行加锁和解锁进行隔离。过程原型如下
lock = threading.RLock() # 创建lock对象 lock.acquire() # 开始锁定 pass # pass就是指我们要执行的语句和操作 lock.release() # 释放锁线程间的通信我们用Event对象,Event实例管理着一个内部标志,通过set()方法会将它设置为True,使用clear()方法会将它重置为False,wait([timeout])方法会使当前线程阻塞标志为True. 下面我们用两个例子进行简单模拟,一个是打印数据每次加30,第二个是两个进程通信
实验效果: 实验代码:
import threading import time class myThread(threading.Thread): def run(self): global x lock.acquire() for i in range(3): x += 10 time.sleep(1) print(x) lock.release() x = 0 lock = threading.RLock() def main(): thrs = [] for item in range(5): thrs.append(myThread()) for item in thrs: item.start() if __name__ == '__main__': main()实验效果 实验代码:
import threading import time class myThreada(threading.Thread): def run(self): evt.wait() print(self.name,':Good morning!') evt.clear() time.sleep(1) evt.set() time.sleep(1) evt.wait() print(self.name,":I'm fine,thank you.") class myThreadb(threading.Thread): def run(self): print(self.name,':Good morning!') evt.set() time.sleep(1) evt.wait() print(self.name,":How are you?") evt.clear() time.sleep(1) evt.set() evt = threading.Event() def main(): John = myThreada() John.name = 'John' Smith = myThreadb() Smith.name = 'Smith' John.start() Smith.start() if __name__ == '__main__': main()使用python的多进程模块可以将工作分配给不受锁定限制的单独子进程。python3对多进程支持的是multiprocessing模块和subprocess模块。使用multiprocessing模块创建和使用多线程,基本上和threading模块的使用方法一致。创建进程使用multiprocessing.Process对象来完成。
下面我们用subprocess创建模块,它用来创建新进程,获取进程的输入、输出以及错误信息。它提供了更高级的接口,可以替换os.system、os.spawn*、popen等,subprocess模块的基本函数如下:
call(args,*,stdin = None,stdout=None,stderr = None,shell=False,timeout=None) # 创建新进程运行程序,输入输出绑定到父进程,返回新进程的退出码 check_call(args,*,stdin = None,stdout=None,stderr = None,shell=False,timeout=None) # 创建新进程运行程序,输入和输出绑定到父进程,退出码为0正常返回 # 否则,返回CalledProcessError getstatusoutput(cmd) # 创建新进程运行程序,元组形式返回新进程退出码和输出 getoutput(cmd) # 创建新进程运行程序,返回新进程输出(字符串) call(args,*,input=None,stdin = None,stdout=None,stderr = None,shell=False, universal_newlines=False,timeout=None) # 创建新进程运行程序,返回新进程的输出(bytesarray) stdin,stdout,stderr 用来处理输入、输出、错误信息shell 是否使用一个中间shell来执行(可以使用shell相关变量等)input 为命令行提供一个输入信息(字符串),不能与stdin同时用universal_newline 返回值和输入值为字符串而不是bytes下面我们用一个例子演示如何操作,
实验准备,新建protest.py,内容为:
print('hello world!')实验效果: 实验代码:
import subprocess print('call() test:',subprocess.call(['python','protest.py'])) print('') print('check_call() test:',subprocess.check_call(['python','protest.py'])) print('getstatusoutput() test:',subprocess.getstatusoutput(['python','protest.py'])) print('') print('getoutput() test:',subprocess.getoutput(['python','protest.py'])) print('') print('check_output() test:', subprocess.check_output(['python','protest.py']))Popen也可以创建新进程,它是一个类,因此初始化参数如下:
class Popen(args, bufsize = -1,executable = None, stdin = None, stdout = None,stderr = None, preexec_fn = None,close_fds = True,shell = False,cwd = None,env = None, universal_newlines = False,startupinfo = None, creationflags = 0, restore_signals = True,start_new_session = False,pass_fds =())因为大部分参数都跟上面类似,我们用到啥就直接等于啥。以下常用方法
poll() 检查子进程是否结束wait(timeout=None) 等待子进程结束communicate(input=None,timeout=None) 用于和紫禁城交互,发送标准输入数据,返回由标准输出和错误输出构成的元组其中常用属性有:
pid 子进程的pidreturncode 子进程的退出码下面用两个例子演示popen创建子进程和communicate函数的效果
实验准备:创建protest.py文件,内容为:
print('hello world!') print(a)实验效果:因a没有赋值,所以会报错 实验代码:
import subprocess prcs = subprocess.Popen(['python','protest.py'],stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) prcs.communicate('These strings are from stdin.') print('subprocess pid:',prcs.pid) print('\nSTDOUT:') print(str(prcs.communicate()[0])) print('STDERR:') print(prcs.communicate()[1])实验准备,创建protest1.py内容为:
a = input() a = a.split(' ') a[0] = str(int(a[0])+1) print(' '.join(a))实验效果 实验代码:
import subprocess processes = [] psum = 5 for i in range(psum): processes.append(subprocess.Popen(['python','protest1.py'],stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True, shell = True)) processes[0].communicate('0 bouquet of flowers!') for before,after in zip(processes[:psum],processes[1:]): after.communicate(before.communicate()[0]) print('\nSum of Process :%d'%psum) print() for item in processes: print(item.communicate()[0])通过线程与进程的学习,能自己创建进程与线程,在后面的学习中,可以对这些不断强化,进而完成掌握的功底!程序员是码出来的,加油!