python进程与线程实例

落花浮王杯 提交于 2020-01-25 20:00:45

运行程序得到的一次运行结果

from random import randint
from time import time, sleep


def download_task(filename):
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))


def main():
    start = time()
    download_task('Python从入门到住院.pdf')
    download_task('Peking Hot.avi')
    end = time()
    print('总共耗费了%.2f秒.' % (end - start))


if __name__ == '__main__':
    main()
开始下载Python从入门到住院.pdf...
Python从入门到住院.pdf下载完成! 耗费了9秒
开始下载Peking Hot.avi...
Peking Hot.avi下载完成! 耗费了6秒
总共耗费了15.01秒.

多进程的方式将两个下载任务放到不同的进程中

from multiprocessing import Process
from os import getpid 
from random import randint
from time import time, sleep

def download_task(filename):
    print('启动下载进程,进程号[%d].' % getpid())
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))

def main():
    start = time()
    p1 = Process(target = download_task, args = ('Python从入门到住院.pdf',))
    p1.start()
    p2 = Process(target = download_task, args = ('Peking Hot.avi', ))
    p2.start()
    p1.join()
    p2.join()
    end = time()
    print('总共耗费了%.2f秒' % (end - start))

if __name__ == '__main__':
    main()
    
启动下载进程,进程号[22482].
开始下载Python从入门到住院.pdf...
启动下载进程,进程号[22487].
开始下载Peking Hot.avi...
Python从入门到住院.pdf下载完成! 耗费了6秒
Peking Hot.avi下载完成! 耗费了7秒
总共耗费了7.04秒

如何实现两个进程间的通信

from multiprocessing import Process
from time import sleep

counter = 0

def sub_task(string):
    global counter
    while counter < 10:
        print(string, end = '', flush = True)
        counter += 1
        sleep(0.01)
        
def main():
    Process(target = sub_task, args = ('Ping', )).start()
    Process(target = sub_task, args = ('Pong', )).start()
    
if __name__ == '__main__':
    main()
PingPongPingPongPingPongPingPongPingPongPingPongPingPongPingPongPingPongPingPong

Python中的多线程–推荐使用threading模块

from random import randint
from threading import Thread
from time import time, sleep

def download(filename):
    print('开始下载%s...' % filename)
    time_to_download = randint(5, 10)
    sleep(time_to_download)
    print('%s下载完成!耗费了%d秒' % (filename, time_to_download))
    
def main():
    start = time()
    t1 = Thread(target = download, args = ('Python从入门到住院', ))
    t1.start()
    t2 = Thread(target = download, args = ('Peding HOt.avi'))
    t2.start()
    t1.join()
    t2.join()
    end = time()
    print('总共耗费了%.3f秒' % (end - start))

if __name__ == '__main__':
    main()
开始下载Python从入门到住院...


Exception in thread Thread-6:
Traceback (most recent call last):
  File "/home/lance/anaconda3/lib/python3.7/threading.py", line 926, in _bootstrap_inner
    self.run()
  File "/home/lance/anaconda3/lib/python3.7/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
TypeError: download() takes 1 positional argument but 14 were given



Python从入门到住院下载完成!耗费了5秒
总共耗费了5.007秒

通过继承Threading模块的Thread类的方式来创建自定义的线程类,然后再创建线程对象并启动线程

from random import randint
from threading import Thread
from time import time, sleep

class DownloadTask(Thread):
    def __init__(self, filename):
        super().__init__()
        self._filename = filename
        
    def run(self):
        print('开始下载%s..' % self._filename)
        time_to_download = randint(5, 10)
        sleep(time_to_download)
        print('%s下载完成! 耗费了 %d 秒' % (self._filename, time_to_download))
        
def main():
    start = time()
    t1 = DownloadTask('Python从入门到住院.pdf')
    t1.start()
    t2 = DownloadTask('Peking hot.avi')
    t2.start()
    t1.join()
    t2.join()
    end = time()
    print('总共耗费了%.2f秒.' % (end-start))

if __name__ == '__main__':
    main()
开始下载Python从入门到住院.pdf..
开始下载Peking hot.avi..
Python从入门到住院.pdf下载完成! 耗费了 5 秒
Peking hot.avi下载完成! 耗费了 7 秒
总共耗费了7.01秒.

一个资源被多个线程竞争使用,那么我们通常称之为“临界资源”

from time import sleep
from threading import Thread


class Account(object):

    def __init__(self):
        self._balance = 0

    def deposit(self, money):
        # 计算存款后的余额
        new_balance = self._balance + money
        # 模拟受理存款业务需要0.01秒的时间
        sleep(0.01)
        # 修改账户余额
        self._balance = new_balance

    @property
    def balance(self):
        return self._balance


class AddMoneyThread(Thread):

    def __init__(self, account, money):
        super().__init__()
        self._account = account
        self._money = money

    def run(self):
        self._account.deposit(self._money)


def main():
    account = Account()
    threads = []
    # 创建100个存款的线程向同一个账户中存钱
    for _ in range(100):
        t = AddMoneyThread(account, 1)
        threads.append(t)
        t.start()
    # 等所有存款的线程都执行完毕
    for t in threads:
        t.join()
    print('账户余额为: ¥%d元' % account.balance)


if __name__ == '__main__':
    main()

账户余额为: ¥2元

通过“锁”来保护“临界资源”

from time import sleep
from threading import Thread


class Account(object):

    def __init__(self):
        self._balance = 0
        self._lock = Lock()

    def deposit(self, money):
        # 先获取锁才能执行后续的代码
        self._lock.acquire()
        try:
            new_balance = self._balance + money
            sleep(0.01)
            self._balance = new_balance
        finally:
            # 在finally中执行释放锁的操作保证正常异常锁都能释放
            self._lock.release()


            
    @property
    def balance(self):
        return self._balance

class AddMoneyThread(Thread):
    def __init__(self, account, money):
        super().__init__()
        self._account = account
        self._money = money
        
    def run(self):
        self._account.deposit(self._money)
    
def main():
    account = Account()
    threads = []
    # 创建100个存款的线程想同一个账户存钱
    for _ in range(100):
        t = AddMoneyThread(account, 1)
        threads.append(t)
        t.start()
    # 等所优存款的线程都执行完毕
    for t in threads:
        t.join()
    print('账户余额为:$%d元' % account.balance)

if __name__ == '__main__':
    main()
账户余额为:$100元

例子1:将耗时间的任务放到线程中以获得更好的用户体验。

import time
import tkinter
import tkinter.messagebox
def download():
    # 模拟下载任务需要花费10s时间
    time.sleep(10)
    tkinter.messagebox.showinfo('tips:', '下载完成!')

def show_about():
    tkinter.messagebox.showinfo('关于', '作者:zhy')
    
def main():
    top = tkinter.Tk()
    top.title('单线程')
    top.geometry('200x150')
    top.wm_attributes('-topmost', True)
    
    panel = tkinter.Frame(top)
    button1 = tkinter.Button(panel, text = 'download', command = download)
    button1.pack(side = 'left')
    button2 = tkinter.Button(panel, text = 'about', command = show_about)
    button2.pack(side = 'right')
    panel.pack(side = 'bottom')
    
    tkinter.mainloop()
    
if __name__ == '__main__':
    main()

使用多线程将耗时间的任务放到一个独立的线程中执行,避免因为执行耗时间的任务而阻塞了主线程

import time
import tkinter
import tkinter.messagebox
from threading import Thread
def main():
    class DownloadTaskHandler(Thread):
        def run(self):
            time.sleep(10)
            tkinter.messagebox.showinfo('提示', '下载完成')
            # 启动下载按钮
            button1.config(state = tkinter.NORMAL)
        
    def download():
        # 禁用下载按钮
        button1.config(state = tkinter.DISABLED)
        # 通过daemon参数将线程设置为守护线程(主程序推出就不再保留执行)
        # 在线程中处理耗时间的下载时间
        DownloadTaskHandler(daemon = True).start()

    def show_about():
        tkinter.messagebox.showinfo('关于', '作者')

    top = tkinter.Tk()
    top.title('单线程')
    top.geometry('200x150')
    top.wm_attributes('-topmost', 1)

    panel = tkinter.Frame(top)
    button1 = tkinter.Button(panel, text = '下载', command = download)
    button1.pack(side = 'left')
    button2 = tkinter.Button(panel, text = '关于', command = show_about)
    button2.pack(side = 'right')
    panel.pack(side = 'right')
    panel.pack(side = 'bottom')

    tkinter.mainloop()
        
if __name__ == '__main__':
    main()
Exception in thread Thread-4:
Traceback (most recent call last):
  File "/home/lance/anaconda3/lib/python3.7/threading.py", line 926, in _bootstrap_inner
    self.run()
  File "<ipython-input-1-7212b40650b5>", line 11, in run
    button1.config(state = tkinter.NORMAL)
  File "/home/lance/anaconda3/lib/python3.7/tkinter/__init__.py", line 1485, in configure
    return self._configure('configure', cnf, kw)
  File "/home/lance/anaconda3/lib/python3.7/tkinter/__init__.py", line 1476, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
RuntimeError: main thread is not in main loop

例子2:使用多进程对复杂任务进行“分而治之”。

from time import time
def main():
    total = 0
    number_list = [x for x in range(1, 100000001)]
    start = time()
    for number in number_list:
        total += number
    print(total)
    end = time()
    print('Execution time: %.3fs' % (end - start))

if __name__ == '__main__':
    main()
5000000050000000
Execution time: 3.151s
from multiprocessing import Process, Queue
from random import randint
from time import time

def task_handler(curr_list, result_queue):
    total = 0
    for number in curr_list:
        total += number
    result_queue.put(total)
    
def main():
    processes = []
    number_list = [x for x in range(1, 100000001)]
    result_queue = Queue()
    index = 0
    # 启动8个进程将数据切片后进行运算
    for _ in range(8):
        p = Process(target = task_handler, 
                    args = (number_list[index:index + 12500000], result_queue))
        index += 12500000
        processes.append(p)
        p.start()
    # 开始记录所有进程执行完成花费的时间
    start = time()
    for p in processes:
        p.join()
    # 合并执行结果
    total = 0

    while not result_queue.empty():
        total += result_queue.get()
    print(total)
    end = time()
    print('Execution time: ', (end - start), 's', sep = '')
if __name__ == '__main__':
    main()
5000000050000000
Execution time: 0.3977470397949219s
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!