分享

用Python创建你第一个GIS程序[5]:多进程解决线程堵塞

 GIS荟 2021-09-19

前言:在上一章中,我们将前后端连接起来,算是真正把程序给创建了出来。但是存在一个问题:当你点击“运行”按钮运行程序后,再用鼠标点击拖动 GUI 界面就会卡死...




线程堵塞

当点击“运行”按钮运行程序后,再用鼠标点击拖动 GUI 界面就会卡死,发生未响应的情况,直到后端代码运行完成后,GUI 界面才会恢复响应。
为什么会出现这种情况呢?
这就是由于发生了线程的堵塞。
比如只有一条道路,前端要走,后端也要走。当后端代码顺利运行后,道路就被堵上了。
这时操作前端,由于路被堵上,前端 GUI 界面出现卡死、未响应。
直到后端代码运行结束,路通了,前端才可以回应我们的操作,如鼠标点击、拖拽等。
 
一个正常的程序在后台运行时,人员也是可以操作图形界面的,甚至再开启一个后台,正常来说不会出现卡死的情况(排除内存泄漏、内存占用过高等情况)。

 
那么要怎样才能实现呢?
 
使用多线程或者多进程,由于 Python 语言现阶段存在 GIL 的情况(没准以后就没有了),使用多线程只对 IO 密集工作有较好的效果。
所以我们这里使用多进程:开启两个进程,相当于有了两条路,GUI 走一条,后端程序走另一条。
 
 

开启多进程

以下的源代码会在重要位置标注注释,如 #<<注释1>>。并且下边会有详细说明。

只需要在 makeGUI.py 文件中添加修改几行代码即可实现。
代码如下:

# -*- coding:utf-8 -*-

import Tkinter as tk

import tkFileDialog

import giscode

from multiprocessing import Process #<<注释1>>

class MyGUI(object):

def __init__(self):

self.root = tk.Tk()

self.root.geometry("450x600+800+200") #设置窗口大小、位置

self.root.title("GIS") #设置程序名称

self.var = tk.StringVar()

# run function

self.create_widget()

self.create_run_button()

self.root.mainloop() # 执行循环

def create_widget(self):

self.frame1 = tk.Frame(self.root, relief="raised", bd=3)

self.frame1.pack(fill="x")

self.entry = tk.Entry(self.frame1)

self.entry.config(textvariable=self.var) #<<注释2>>

self.entry.pack(side="left",expand=True,

fill="x", pady=8, padx=10)

self.but = tk.Button(self.frame1, text=u"输入线要素",

relief="groove", width=10)

self.but.config(command=self.open_dialog)

self.but.pack(side="right", pady=8)

def open_dialog(self):

varrr = tkFileDialog.askopenfilename()

self.var.set(varrr)

def create_run_button(self):

# 生成下方的“运行”按钮

self.bottom_frame = tk.Frame(self.root,relief="raised",bd=3)

self.bottom_frame.pack(side="bottom",fill="x",anchor="s")

self.ok_button =tk.Button(

self.bottom_frame,text=u"运行",relief="groove", width=10)

self.ok_button.pack(side="right", pady=8)

self.ok_button.config(command=self.run_multiprocessing) #<<注释2>>

# def run(self):

# giscode.main(self.var.get())

def run_multiprocessing(self): #<<注释3>>

p = Process(target=giscode.main,

args=(self.var.get(),))

p.start()

print "PID:",p.pid

if __name__ == '__main__':

MyGUI()

<<注释1>>:multiprocessing 是 Python 自带的多进程管理库。其中的 Process 模块用于创建一个新的进程对象。
 
<<注释2>>:将点击按钮触发的  run 方法改成了一个新建的 run_multiprocessing 方法。该方法是新创建的,作用是创建一个新进程来运行后端的代码。
 
<<注释3>>:创建一个方法便于按钮点击触发。Process 是一个类,用于创建一个新的进程对象,常用的参数是 target 和 args 参数。
target:接收名字。
target 参数接收函数、方法或者类的名字,不加括号。在使用 Process 类中 start 后,新创建的进程就会执行 target 接收的参数。
args:接收元组参数。
既然 target 接收函数、方法或者类,那么如果这些函数、方法或者类也存在参数怎么办呢?所以这里使用 args 接收获得参数。接收形式为参数组成的元组:args=(var1, var2)。

Note: 接收单个参数要加逗号,如:args=(var1,) 加一个逗号避免产生歧义,否则会导致字符串被拆分成元组,比如 "use" 被拆分成三部分 ("u","s","e")
 

执行程序

使用最新修改后的 makeGUI.py 文件即可。
 

结束语

第五章的源代码和演示用矢量文件下载地址:
链接:https://pan.baidu.com/s/1JMo5h16DihUjpdGrDCEOKg  
提取码:pu9h  

分享GIS,不止于Python。
荟GIS精粹,关注我,带你飞!
我叫 GIS荟

欢迎交流

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多