主线程与子线程之间相互通信(HandlerThread)
Posted 贺兰猪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主线程与子线程之间相互通信(HandlerThread)相关的知识,希望对你有一定的参考价值。
平时,我们一般都是在子线程中向主线程发送消息(要在主线程更新UI),从而完成请求的处理。那么如果需要主线程来向子线程发送消息,希望子线程来完成什么任务。该怎么做?这就是这篇文章将要讨论的内容。
一、HandlerThread类
主线程发送消息给子线程,通常思维逻辑就是:创建子线程,在主线程中实例化一个与子线程Looper相关联的Handler,这样handler处理的消息就是该子线程中的消息队列,而处理的逻辑都是在该子线程中执行的,不会占用主线程的时间。
实践一下。新建项目,修改它的MainActivity的代码,如下即可:
public class ThreadHandlerActivity extends Activity
//创建子线程
class MyThread extends Thread
private Looper looper;//取出该子线程的Looper
@Override
public void run()
Looper.prepare();//创建该子线程的Looper
looper = Looper.myLooper();//取出该子线程的Looper
Looper.loop();//只要调用了该方法才能不断循环取出消息
private TextView tv;
private MyThread thread;
private Handler mHandler;//将mHandler指定轮询的Looper
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Handler实验");
setContentView(tv);
thread = new MyThread();
thread.start();//千万别忘记开启这个线程
//下面是主线程发送消息
mHandler = new Handler(thread.looper)
@Override
public void handleMessage(android.os.Message msg)
Log.d("当前子线程是----->", Thread.currentThread()+"");
;
;
mHandler.sendEmptyMessage(1);
现在运行该程序。并没有得到预期的结果呢,报错误如下:
这是一个空指针错误。当主线程走到mHandler = new Handler(thread.looper),此时子线程的Looper对象还没有被创建出来,那么此时thread.looper肯定为空了。
当然了,你可以让主线程休眠2秒后再执行后面的代码,但是如果有很多个子线程都需要主线程类给其分配任务怎么办??那简直要乱套了。所以需要一个更好的解决方式。Android显然也考虑到了这个问题,于是它我们提供了一个HandlerThread类。这个类是专门处理这个问题的。
当主线程中有耗时的操作时,需要在子线程中完成,通常我们就把这个逻辑放在HandlerThread的对象中执行(该对象就是一个子线程)一下就可以了。下面我们就修改上面的代码,看一看如何使用HandlerThread这个类。修改MainActivity中的代码如下:
public class ThreadHandlerActivity extends Activity
private TextView tv;
private Handler mHandler;//将mHandler指定轮询的Looper
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Handler实验");
setContentView(tv);
//实例化一个特殊的线程HandlerThread,必须给其指定一个名字
HandlerThread thread = new HandlerThread("handler thread");
thread.start();//千万不要忘记开启这个线程
//将mHandler与thread相关联
mHandler = new Handler(thread.getLooper())
public void handleMessage(android.os.Message msg)
Log.d("当前子线程是----->", Thread.currentThread()+"");
;
;
mHandler.sendEmptyMessage(1);//发送消息
运行程序,打印的结果如下:
从打印结果来看,当前子线程的名字正是我们所起的那个名字“handler thread"。
你会有疑问,表面上看HandlerThread并没有创建自己的Looper啊?而且既然是一个线程,那么我们肯定也能重写它的run方法吧。在解答你的疑问之前,我们不妨重写它的run方法来看一看会有什么结果。将代码修改如下:
public class ThreadHandlerActivity extends Activity
private TextView tv;
private Handler mHandler;//将mHandler指定轮询的Looper
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Handler实验");
setContentView(tv);
//实例化一个特殊的线程HandlerThread,必须给其指定一个名字
HandlerThread thread = new HandlerThread("handler thread")
@Override
public void run()
for(int i=0;i<3;i++)
Log.d("handler thread run ",i+"");
;
// HandlerThread thread = new HandlerThread("handler thread");
thread.start();//千万不要忘记开启这个线程
//将mHandler与thread相关联
mHandler = new Handler(thread.getLooper())
public void handleMessage(android.os.Message msg)
Log.d("当前子线程是----->", Thread.currentThread()+"");
;
;
mHandler.sendEmptyMessage(1);//发送消息
红色部分就是我们重写了它的run方法。再云运行程序,打印的结果如下:
for循环的打印结果正常,但是为什么没有打印出”当前子线程“呢。其实这正是我们要解释的地方。查看HandlerThread的源代码,找到它的run方法,如下:
@Override
public void run()
mTid = Process.myTid();
Looper.prepare();
synchronized (this)
mLooper = Looper.myLooper();
notifyAll();
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
可以知道,在run方法中实例化自己的Looper,如果继续追踪源代码翻看其getLooper方法你会发现,如果一个Handler在与HandlerThread进行绑定时,发现Looper为空,Handler则会一直等待直到Looper被创建出来为止,然后才继续执行后续的代码。所以我们重写了HandlerThread的run方法,肯定就不会去创建Looper对象,那么绑定的Handler就会永远处于等待状态,自然而然就不会打印出”当前子线程“信息了。这也是为什么我们要使用HandlerThread这个特殊的线程,因为使用这个,我们不必关心多线程会混乱,Looper会为空等一系列问题,只要去关心我们要实现的逻辑就行了。
好了,现在做一下简单的总结吧。
- 1. Handler与哪个线程的Looper相关联,那么它的消息处理逻辑就在与之相关的线程中执行,相应的消息的走向也就在相关联的MessageQueue中。(最常见的就是Handler与主线程关联,那么接收Looper回传的消息后的逻辑就会在主线程中执行)
- 2. 当主线程中需要与子线程进行通信时(比如将耗时操作放在子线程中),建议使用HandlerThread。同时要注意,千万不要去重写它的run方法。
二、一个主线程与子线程互相通信的例子
知识点都说完了。下面我们来写一个具体的例子实践一下吧。新建一个项目,修改它的MainActivity代码,如下:
public class ThreadHandlerActivity extends Activity
private TextView tv;
private Handler mHandler;//与子线程关联的Handler
private Handler handler;//与主线程关联的Handler
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText("Handler实验");
setContentView(tv);
//实例化一个特殊的线程HandlerThread,必须给其指定一个名字
HandlerThread thread = new HandlerThread("handler thread");
thread.start();//千万不要忘记开启这个线程
//将mHandler与thread相关联
mHandler = new Handler(thread.getLooper())
public void handleMessage(android.os.Message msg)
Log.d("我是子线程----->", Thread.currentThread()+"");
handler.sendEmptyMessage(1);//发送消息给主线程
;
;
handler = new Handler()
public void handleMessage(android.os.Message msg)
Log.d("我是主线程----->", Thread.currentThread()+"");
mHandler.sendEmptyMessage(1);//发送消息给子线程
;
;
mHandler.sendEmptyMessage(1);//发送消息
handler.sendEmptyMessage(1);//发送消息
注释很详细,不解释 了。运行程序,结果如下:
这样子,就会一直循环下去,轮流打印出主线程和子线程。
python 多线程中子线程和主线程相互通信
主线程开启多个线程去干活,每个线程需要完成的时间不同,干完活以后都要通知给主线程,下面代码说明该应用:
代码块:
import threading import queue import time import random ‘‘‘ 需求:主线程开启了多个线程去干活,每个线程需要完成的时间 不同,但是在干完活以后都要通知给主线程 多线程和queue配合使用,实现子线程和主线程相互通信的例子 ‘‘‘ q = queue.Queue() threads=[] class MyThread(threading.Thread): def __init__(self,q,t,j): super(MyThread,self).__init__() self.q=q self.t=t self.j=j def run(self): time.sleep(self.j) # 通过q.put()方法,将每个子线程要返回给主线程的消息,存到队列中 self.q.put("我是第%d个线程,我睡眠了%d秒,当前时间是%s" % (self.t, self.j,time.ctime())) ‘‘‘ # 生成15个子线程,加入到线程组里, # 每个线程随机睡眠1-8秒(模拟每个线程干活时间的长短不同) ‘‘‘ for i in range(15): j=random.randint(1,8) threads.append(MyThread(q,i,j)) # 循环开启所有子线程 for mt in threads: mt.start() print(‘进程开启时间:%s‘%(time.ctime())) ‘‘‘ 通过一个while循环,当q队列中不为空时,通过q.get()方法, 循环读取队列q中的消息,每次计数器加一,当计数器到15时, 证明所有子线程的消息都已经拿到了,此时循环停止 ‘‘‘ count = 0 while True: if not q.empty(): print(q.get()) count+=1 if count==15: break
以上是关于主线程与子线程之间相互通信(HandlerThread)的主要内容,如果未能解决你的问题,请参考以下文章