python线程和进程操作(含例子学习)

线程

线程是CPU使用的基本单元,由主线程来创建,并使用这个进程的资源。python中thread模块提供支持,通过以下两种方法创建线程:

  • 通过threading.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试试。

通过继承threading.Thread类来创建线程

这个创建类来继承就行了,了解面向对象的同学应该明白,肯定有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使用

上面的例子我们只是用thread里的init和run方法,还有几个重要的方法和属性一起熟悉一下:
方法:

  • join([timeout])
  • isAlive()

属性:

  • name
  • daemon
# 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()

将打印平方的工作,都交到后台去了,这里不做执行

RLock与Event讲解

线程的隔离性就不需要彼此修改,因此产生锁的概念。python中threading模块中的对象Lock和RLock进行简单的同步,对于同一个时刻只允许一个线程操作的数据对象,可以进行加锁和解锁进行隔离。过程原型如下

lock = threading.RLock() # 创建lock对象
lock.acquire() # 开始锁定
pass # pass就是指我们要执行的语句和操作
lock.release() # 释放锁

线程间的通信我们用Event对象,Event实例管理着一个内部标志,通过set()方法会将它设置为True,使用clear()方法会将它重置为False,wait([timeout])方法会使当前线程阻塞标志为True.
下面我们用两个例子进行简单模拟,一个是打印数据每次加30,第二个是两个进程通信

例子:RLock对线程数据加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()


例子:模拟两个线程通过Event进行通信

实验效果
在这里插入图片描述
实验代码:

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

下面我们用一个例子演示如何操作,

例子:用进程打开py文件,并输出

实验准备,新建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类创建进程

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 子进程的pid
  • returncode 子进程的退出码

下面用两个例子演示popen创建子进程和communicate函数的效果

例子:popen产生子进程,执行py源代码

实验准备:创建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])

例子:communicate实现进程数据传递

实验准备,创建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])

总结

通过线程与进程的学习,能自己创建进程与线程,在后面的学习中,可以对这些不断强化,进而完成掌握的功底!程序员是码出来的,加油!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章