Python多线程3 多线程的传参与返回值

学习来源

在主线程用一个变量或者直接输出就能获取或使用函数中return的值。但是在多线程中,可以这么理解,多线程之间的变量空间是互相隔绝的,所以return是不能把值返回到主进程的,只能在所在的线程使用,线程结束,值所在空间也就被释放了。所以,多线程之间需要一个更加全局性的存储器来保存所有线程之间的值,这里使用queue(队列)来完成这项工作。

什么是队列?

简单介绍一下队列,队列是一种存储结构,就像一个水管一样,给这根水管规定一个方向,只能从一头写入(进水),只能从另一头读出(出水)。

存满了怎么办?

采用动态机制,每存一项,我找一块空间,标记好上一次存储的队列头部;然后把新存入的这项声明为整个队列新的头部。从逻辑上讲,这还是那个队列,动态的增加了一节长度(通过地址指向连接的方式),所以,它是不会满的。(不考虑物理存储空间不够的情况)

读出来在队列中就没了。

下面程序中从队列中读出的命令是q.get(),这个函数不用传参,不像列表那样通过索引取值。上面也提到了,队列只能从一端取值,每次只能取尾部的一个值,这里的取值动作相当于 拿出来,之后尾部会向前移动一个单位,这样也就再能取下一个值了,通过这样的机制,使队列成为了一种先进先出的数据结构。

上程序

直接return是不行滴,用一个全局queue(队列)来实现值传递

导入支持包from queue import Queue

声明一个queueq = Queue()

向队列中存值q.put(值)

从队列中取值q.get()

上面的程序就是每个线程处理一段数据,将处理结果存入队列,所有线程都结束之后,从队列中依次取出各线程的操作结果,并print出来。

会发现上面的程序中写了三个for循环,其循环结构都是一样的,都是针对4个线程,每轮循环处理一个线程,但是它们确实是不能合在一块的,至少for2和for3是不能放在for1中的。

如果把for2中的.join()加到for1中,第一轮第一个线程.join之后,后面的会等第一个线程结束之后才会执行,则在第一个线程完成之前不会去生成第二个线程的,同理,后面的线程都会等待前面的线程完成之后才会生成。这样程序就变成了串行的(为了看到效果可以在job()函数中做一段时间延迟(time.sleep()),就会发现把.join()加到for1中,执行时间变成了原来的4倍(因为有4个线程串行)),多线程也就没有了意义。

如果把for3的代码加到for1中,results.append(q.get())是主线程的操作,它不会等待非主线程(上面刚提到for1中不能.join()),如果非主线程耗时比较长,则会发生队列还没有被写入(q.put(值))就已经开始读(q.get())的错误。

测试了for2和for3合并起来是不会影响效率的,但是也只局限于这个程序,实际上这样搞也是不科学的。在这个程序中,results.append()每次append的都是这一轮线程的,如果要操作到之后线程的结果,因为之后的线程没有.join(),就有可能发生取不到值的情况。

综上,写成三个for还是很有必要的。也就是如果要操作多个线程的结果,严格分成 线程声明执行进程.join()操作多个进程结果三步才是安全且有必要的。

You may also like...

1 Response

  1. 2020年1月15日

    […] 对于在当前主进程的操作,函数的返回值可以直接操作,或者用一个参量进行接收。但是在其他进程中运行的函数的返回值,是无法直接传递到主进程的。将其返回值存到一个全局性的存储器中,是一种可行的方案。这里用queue(队列)来存储多个进程的返回值。在主进程中可以将他们依次取出,这样就做到了多进程与主进程的返回值传递。关于队列,我在我的这篇博客中做了简单说明,该博客也是多线程中返回值传递的介绍。 […]

发表评论

电子邮件地址不会被公开。 必填项已用*标注