欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python线程和进程操作(含代码例子)

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

线程

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

  • 通过threading.Thread直接在线程中运行函数
  • 通过继承threading.Thread类来创建线程

用threading.Thread直接在线程中运行函数

这里我们就用这个函数创建一个线程,简单模拟打印平方,这个函数原型我们首先熟悉一下,尤其是各个参数

Thread(group=None, target=None, Name=None, args=(), kwargs={}, *, daemon = None)
# 其中target参数就是运行的函数,args是传入函数的参数元组。

例子:并行打印平方

运行效果:(并行效果哟!)
python线程和进程操作(含代码例子)

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函数里,因为它继承可以重载。我们还是仍以求平方为例子

例子:继承类并行打印平方

实验效果:
python线程和进程操作(含代码例子)
实验代码:

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的使用

例子:同步执行平方

实验效果:
python线程和进程操作(含代码例子)
实验代码:

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结束后运行。

例子:将函数转到后台执行不做输出

实验效果:
python线程和进程操作(含代码例子)
实验代码:

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后输出

实验效果:
python线程和进程操作(含代码例子)
实验代码:

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进行通信

实验效果
python线程和进程操作(含代码例子)
实验代码:

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!')

实验效果:
python线程和进程操作(含代码例子)
实验代码:

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没有赋值,所以会报错
python线程和进程操作(含代码例子)
实验代码:

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))

实验效果
python线程和进程操作(含代码例子)
实验代码:

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])

总结

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

本文地址:https://blog.csdn.net/m0_37149062/article/details/107049354