概述
一说进程,很多人就想要问线程,那么就先说下两者的不同吧
其实进程就是一个个的程序
那线程其实是进程里面的一个个子功能
在举例子来说明,我们以前在电脑前挂QQ,多个qq其实就是一个不同的进程,进程之间是相互独立运行的,也就是每一个进程都会消耗一份系统的资源。
那么线程呢,就比如说我们点开qq了,看到里面的各个功能,有什么聊天,小程序,发空间等等这些,他们就相当于一个个线程,也就是说,其实,进程里面是包含着线程的,并且,线程是的资源是共享的。
常用方法
我在工作过程中,主要使用的是进程,那能避免使用线程,我还是避免使用的,因为资源的共享,会导致出现很多的问题。
那接下来,我就直接说一下进程的简单使用吧:
首先在python中使用的库
from multiprocessing import Process,Manager,pool,Value
一般我是这么导入,和用到如下的库。
那么现在浅显的说一下上面的几个函数的使用方法吧
首先Process,就是启动一个进程,
p = process(参数)
p.start()
其次,Manager,Value的话,主要是多个进程如果使用同一个局部变量的话,就需要使用上面这两个函数
Manager有很多的方法,我主要使用dict和list两种,当然,如果dicct的话,会有一个坑,就是深层复制,无法复制成功,需要由外层进行引导。
最后pool的话是开始一个进程池,这个的话,其实使用方法大致和Process类似。
python multiprocessing
说明几点:
- 首先,在进程里面是可以在接着开进程的。
其实进程和线程可以搭配着使用,但是这样就会比较乱。 - 资源共享需要事先声明,然后不管你开多少个进程,都是共享的,但是,千万不要再进程里面再去申明进程。
- 资源共享Manager.dict()是有坑在里面的,现在接下来就说明这个问题
如果需要对dict的深层赋值,我们只能曾浅层开始一步步传值。
当时这片文章的一个理解,对我帮助特别大:
https://segmentfault.com/a/1190000018619281
我自己也写了如下代码,浅层次,从使用的角度,验证了一下:
from multiprocessing import Process,Manager
import datetime
class TestMul(object):
def __init__(self):
self.balck = Manager().dict()
self.freeze = Manager().dict()
self.ips = [
"192.168.111.7",
"192.168.111.8",
"192.168.112.1",
"192.168.112.2",
]
def forall_sleep(fn):
def inloop(self,*args):
for ip in self.ips:
print ip
fn(self,ip,*args)
return inloop
def chose_dict(self):
pass
def decision(self,probelm):
type_anmorl = {
"fullblack": self.balck,
"halfblack": self.freeze,
}
return type_anmorl[probelm]
@forall_sleep
def ip_write1(self,ip,problem):
problem_dict,problem_type = self.decision(problem)
if not problem_dict.has_key(ip):
self.writ_dict2(problem_dict,ip)
print problem_dict
else:
problem_dict["test"]["time"] +=1
print problem_dict
return problem_dict
# 问题的举例
def writ_dict1(self,dict_massage,ip):
dict_massage[ip] = str(datetime.datetime.now())
dict_massage["test"] = {}
row = dict_massage["test"]
row["time"] = 1
dict_massage["test"] = row
dict_massage["a"] = {}
dict_massage["a"]["b"] = str(datetime.datetime.now())
print dict_massage
def writ_dict2(self,dict_massage,ip):
dict_massage[ip] = str(datetime.datetime.now())
dict_massage["test"] = {}
row = dict_massage["test"]
row["time"] = 1
dict_massage["test"] = row
dict_massage["a"] = {}
raw = dict_massage["a"]
raw["b"] = str(datetime.datetime.now())
dict_massage["a"] = raw
print dict_massage
if __name__ == '__main__':
t = TestMul()
a = t.decision(probelm = "fullblack")
# print a
# t.ip_write1("fullblack") #这个是一般装饰器的用法
t.writ_dict1(a,"192.168.112.2")
t.writ_dict2(a, "192.168.112.2")
结果如下:
{'test': {'time': 1}, 'a': {}, '192.168.112.2': '2021-01-01 17:18:49.486341'}
{'test': {'time': 1}, 'a': {'b': '2021-01-01 17:18:49.487344'}, '192.168.112.2': '2021-01-01 17:18:49.487202'}
直接看Manager生成的不同的代码就可以。
我当时还写了一个装饰器,那这里就额外对装饰的使用进行一个初步的说明吧。
装饰器其实就是简化了我们的一个使用,我上面写的装饰器,其实不太正规,但是,可以用来加深对装饰器的一个理解。
别人的文章会对装饰器多一个深度的剖析,但是,我们工作中,根本用不到那些东西。只需要把用到的掌握好就可以。
其实装饰主要是传参数和不传参数的,我们主要看最里面的那层嵌套就好了
def forall_sleep(fn):
def inloop(self,*args):
for ip in self.ips:
print ip
fn(self,ip,*args)
return inloop
首先,说一下*args
一般情况呢,我们还可以带一个**kwargs
这里我没带,但我都说一下,一个传入的是不定长的参数,一个字典型的参数,这两个呢,其实是对应你原函数的一个参数的。
接下来,要关注的是
fn(self,ip,*args)
t.ip_write1("fullblack")
fn其实是代表,原函数的,后面是他要调用的参数,这个是要跟原函数一一对应的。
能知道,我为什么就传入一个参数了吗?原函数可以两个呢?
这也是我为什么说,我写的这个装饰器不正规的原因。因为他不通用。
@forall_sleep
def ip_write1(self,ip,problem):
problem_dict,problem_type = self.decision(problem)
if not problem_dict.has_key(ip):
self.writ_dict2(problem_dict,ip)
print problem_dict
因为这个ip,我放在装饰器中去了。
关于使用装饰器,函数是怎么调用的,我推荐大家看我的另一篇文章,当然,也不是我的自卖自夸。
https://blog.csdn.net/weixin_43635231/article/details/110442738
接着说,上面,进程dict的解决办法,其实就是改成这么调用就可以了
#例子:
def test(idx, test_dict):
test_dict['test'][idx] = idx
## 正确的使用方式
def test(idx, test_dict):
row = test_dict['test']
row[idx] = idx
test_dict['test'] = row
确实有点麻烦,但是却又是无可奈何