精华内容
下载资源
问答
  • 2018-03-04 18:48:16

            线程交互是指两个线程之间通过通信联系对锁的获取与释放,从而达到较好的线程运行结果,避免引起混乱的结果。一般来说synchronized块的锁会让代码进入同步状态,即一个线程运行的同时让其它线程进行等待,那么如果需要进行实现更复杂的交互,则需要学习以下几个方法:

    void notify():唤醒在此对象监视器上等待的单个线程。

    void notifyAll():唤醒在此对象监视器上等待的所有线程。

    void wait():让占用了这个同步对象的线程,临时释放当前的占用,并且等待。

            wait()方法是使当前线程临时暂停,释放锁,并进入等待,其功能类似于sleep()方法,但是wait()需要释放锁,而sleep()不需要释放锁。以下通过例子说明wait()方法实现线程间的交互,举例子之前需要强调的是wait()方法一定存在于synchronized作用范围内,否则报错,因为它是同步对象上的方法。首先定义一个学生类:

    1.理解wait()和notify()的相互关系

            首先该类有两个同步方法,存钱和取钱,首先需要保证当取钱取到只剩0元时,当前取钱的线程应该进入等待状态,此时调用同步对象的notify状态,我们为了观察notify()方法的作用,首先不使用它,进行对比:

    package person;
    import java.util.concurrent.locks.*;
    public class Student {
    	public int k=0;
    	public int total;
    	public int perMonth;
    	public String name;
    	Lock lock=new ReentrantLock();
    	public synchronized void saveMoney(Student s) {
    		// TODO Auto-generated method stub
    		
    		s.total=s.total+2*(s.perMonth);
    		System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
    		
    		
    		
    	}
    	public synchronized void getMoney(Student s)
    	{
    		if(total<=0)
    		{
    			try
    			{
    				this.wait();//让占有this的取钱过程,暂时释放对this的占有,并等待,等待其它线程的调用同步对象的notify方法来唤醒它。
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    		s.total=s.total-s.perMonth;
    		System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	
    
    
    	}
    
    }

    测试程序如下,这个测试程序在下文中不改变:

    package waytobuildthread;
    
    import person.Student;
    
    public class RunnableTest {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		final Object obj=new Object();
    		final Student s=new Student();
    		final Student jack=new Student();
    		s.total=1000;
    		s.perMonth=200;
    		s.name="Tom";
    		System.out.println("Tom最开始拥有"+s.total+"元");
    		Thread[] save=new Thread[6];
    		Thread[] get=new Thread[10];
    		for(int i=0;i<10;i++)
    		{
    			Thread t1= new Thread(){
    	            public void run(){
    	            	
    	            	
    	            	s.getMoney(s);;
    	            	
    	        		try {
    	        			Thread.sleep(1000);;
    	        		}
    	        		catch(InterruptedException e) {
    	        			e.printStackTrace();
    	        		}
    	            	
    	            }
    	        };
    	        t1.start();
    	        get[i]=t1;
    		}
    		
    		for(int i=0;i<6;i++)
    		{
    			Thread t2= new Thread(){
    	            public void run(){
    	            	
    	            	s.saveMoney(s);
    	            	
    	            	
    	        		try {
    	        			Thread.sleep(1000);;
    	        		}
    	        		catch(InterruptedException e) {
    	        			e.printStackTrace();
    	        		}
    	            	
    	            }
    	        };
    	        t2.start();
    	        save[i]=t2;
    		}
    		//等待线程结束
    		for(Thread t:save)
    		{
    			try
    			{
    				t.join();
    				
    			}
    			catch(InterruptedException e)
    			{
    				e.printStackTrace();
    			}
    
    		}
    		//等待线程结束
    		for(Thread t:get)
    		{
    			try
    			{
    				t.join();
    			}
    			catch(InterruptedException e)
    			{
    				e.printStackTrace();
    			}
    
    		}
    		
    		System.out.println("最后Tom总共拥有"+s.total+"元");
    		
    	}
    
    }
    

    运行结果如下:

    Tom最开始拥有1000元
    Tom取了200当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom存了400当前总共拥有400元
    Tom存了400当前总共拥有800元
    Tom存了400当前总共拥有1200元
    Tom存了400当前总共拥有1600元
    Tom存了400当前总共拥有2000元
    Tom存了400当前总共拥有2400元
    

            从上面结果可以看出,当取钱取到只剩0元时,取钱线程便释放对this的占有,进入等待,但是由于没有唤醒等待线程的方法notify()方法,所以取钱线程便不会被唤醒,因此剩余的取钱线程不会被执行。那么下面我们在存钱方法中添加notify()方法,用于唤醒等待中的取钱方法:

    package person;
    import java.util.concurrent.locks.*;
    public class Student {
    	public int k=0;
    	public int total;
    	public int perMonth;
    	public String name;
    	Lock lock=new ReentrantLock();
    	public synchronized void saveMoney(Student s) {
    		// TODO Auto-generated method stub
    		
    		s.total=s.total+2*(s.perMonth);
    		System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
    		this.notify();//用于唤醒暂停中的线程
    		
    		
    		
    	}
    	public synchronized void getMoney(Student s)
    	{
    		if(total<=0)
    		{
    			try
    			{
    				this.wait();//让占有this的取钱过程,暂时释放对this的占有,并等待,等待其它线程的调用同步对象的notify方法来唤醒它。
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    		s.total=s.total-s.perMonth;
    		System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	
    
    
    	}
    
    }
    

    运行测试程序,结果如下:

    Tom最开始拥有1000元
    Tom取了200当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom存了400当前总共拥有400元
    Tom存了400当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom存了400当前总共拥有1000元
    Tom取了200当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom存了400当前总共拥有800元
    Tom存了400当前总共拥有1200元
    Tom取了200当前总共拥有1000元
    Tom存了400当前总共拥有1400元
    最后Tom总共拥有1400元
    

            对比两者关系可以看出notify的作用,上面例子说明当取钱取到只剩0元时,便会进入暂停,此时等待着的存钱线程便会被执行,每次执行之后便会唤醒暂停着的线程,使其进入等待状态。这就是notify()的作用。

    2.深入交互

            以上只是对取钱进行了临时暂停,释放占有,那么接下来如果对存钱过程也添加wait()方法又会怎样的呢?以下便是对其进行了两个同步方法都添加了暂停与唤醒功能,如下所示:

    package person;
    import java.util.concurrent.locks.*;
    public class Student {
    	public int k=0;
    	public int total;
    	public int perMonth;
    	public String name;
    	Lock lock=new ReentrantLock();
    	public synchronized void saveMoney(Student s) {
    		// TODO Auto-generated method stub
    		if(total>=2000)
    		{
    			try
    			{
    				this.wait();
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		s.total=s.total+2*(s.perMonth);
    		System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
    		this.notify();
    		
    		
    	}
    	public synchronized void getMoney(Student s)
    	{
    		if(total<=0)
    		{
    			try
    			{
    				this.wait();
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    		s.total=s.total-s.perMonth;
    		System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	
    		this.notify();
    
    
    	}
    
    }
    

    运行结果如下:

    Tom最开始拥有1000元
    Tom取了200当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom存了400当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom取了200当前总共拥有-200元
    Tom存了400当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom存了400当前总共拥有400元
    Tom存了400当前总共拥有800元
    Tom存了400当前总共拥有1200元
    Tom取了200当前总共拥有1000元
    Tom存了400当前总共拥有1400元
    最后Tom总共拥有1400元

            观察上述结果,会发现结果并未如我们期待那样,出现了一个负值,这是为什么呢因为当取钱取到只剩0时,接下去又调用了一个notify()方法,可能唤醒了另一个取钱线程,即继续取钱,所以会出现负值,那么怎么解决这一问题呢?很简单,就是将if改为while,表示如果账户余额如果不大于0,则不会取钱。则一直等待。同理存钱有上限,所以如果余额不小于上限,那么也会一直等待。因此,修改如下所示:

    package person;
    import java.util.concurrent.locks.*;
    public class Student {
    	public int k=0;
    	public int total;
    	public int perMonth;
    	public String name;
    	Lock lock=new ReentrantLock();
    	public synchronized void saveMoney(Student s) {
    		// TODO Auto-generated method stub
    		while(total>=2000)
    		{
    			try
    			{
    				this.wait();
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		s.total=s.total+2*(s.perMonth);
    		System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
    		this.notify();//唤醒那些等待着取钱的过程,可以醒过来了。
    		
    		
    	}
    	public synchronized void getMoney(Student s)
    	{
    		while(total<=0)
    		{
    			try
    			{
    				this.wait();//让占有this的取钱过程,暂时释放对this的占有,并等待,等待其它线程的调用同步对象的notify方法来唤醒它。
    			}
    			catch(InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    		s.total=s.total-s.perMonth;
    		System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");	
    		this.notify();
    
    
    	}
    
    }
    
    测试结果如下:
    Tom最开始拥有1000元
    Tom取了200当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom取了200当前总共拥有0元
    Tom存了400当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom存了400当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom取了200当前总共拥有200元
    Tom存了400当前总共拥有600元
    Tom取了200当前总共拥有400元
    Tom存了400当前总共拥有800元
    Tom取了200当前总共拥有600元
    Tom存了400当前总共拥有1000元
    Tom存了400当前总共拥有1400元
    最后Tom总共拥有1400元
    
            根据上述结果可以知道,我们在进行多线程交互时,如果要对共享数据有约束条件,需要注意对wait()方法与notify()方法使用的时机与条件。    
            总之,wait()、notify()、notifyAll()三个方法必须在synchronized代码块中使用,否则会报错,另外,如果使用了wait()方法,如果想再次使用该方法,当前同步方法,则需要使用notify()唤醒。
    更多相关内容
  • #资源达人分享计划#
  • 基于分区缓存区重放与多线程交互的多智能体深度强化学习算法.pdf
  • android 的service和activity是运行在UI主线程的。在android线程中,只有主线程即UI线程有自己的默认的消息队列
  • 基于多线程交互的Android动态视点随动技术.pdf
  • java编写的多线程交互示例程序,轻松理解java线程同步。
  • 行业分类-设备装置-基于ZigbeeWifi3G通信的双屏多线程交互教学平台
  • android 的service和activity是运行在UI主线程的。在android线程中,只有主线程即UI线程有自己的默认的消息队列。子线程需要创建自己的消息队列,并把消息发给队列,并循环起来,发给handler处理。
  • C#多线程编程实例 线程与窗体交互源码,该示例演示如何在线程安全的模式下调用Windows窗体上的控件。
  • Kotlin入门(30)多线程交互

    千次阅读 2018-10-29 21:56:36
    Android开发时常会遇到一些耗时的业务场景,比如后台批量处理数据、访问后端服务器接口等等,此时为了保证界面交互的及时响应,必须通过线程单独运行这些耗时任务。简单的线程可使用Thread类来启动,无论Java还是...

    Android开发时常会遇到一些耗时的业务场景,比如后台批量处理数据、访问后端服务器接口等等,此时为了保证界面交互的及时响应,必须通过线程单独运行这些耗时任务。简单的线程可使用Thread类来启动,无论Java还是Kotlin都一样,该方式首先要声明一个自定义线程类,对应的Java代码如下所示:

        private class PlayThread extends Thread {
            @Override
            public void run() {
                //此处省略具体的线程内部代码
            }
        }

    自定义线程的Kotlin代码与Java大同小异,具体见下:

        private inner class PlayThread : Thread() {
            override fun run() {
                //此处省略具体的线程内部代码
            }
        }

    线程类声明完毕,接着要启动线程处理任务,在Java中调用一行代码“new PlayThread().start();”即可,至于Kotlin则更简单了,只要“PlayThread().start()”就行。如此看来,Java的线程处理代码跟Kotlin差不了多少,没发觉Kotlin比Java有什么优势。倘使这样,真是小瞧了Kotlin,它身怀多项绝技,单单是匿名函数这招,之前在介绍任务Runnabe时便领教过了,线程Thread同样也能运用匿名函数化繁为简。注意到自定义线程类均需由Thread派生而来,然后必须且仅需重写run方法,所以像类继承、函数重载这些代码都是走过场,完全没必要每次都依样画葫芦,编译器真正关心的是run方法内部的具体代码。于是,借助于匿名函数,Kotlin的线程执行代码可以简写成下面这般:

        Thread {
            //此处省略具体的线程内部代码
        }.start()

    以上代码段看似无理,实则有规,不但指明这是个线程,而且命令启动该线程,可谓是简洁明了。线程代码在运行过程中,通常还要根据实际情况来更新界面,以达到动态刷新的效果。可是Android规定了只有主线程才能操作界面控件,分线程是无法直接调用控件对象的,只能通过Android提供的处理器Handler才能间接操纵控件。这意味着,要想让分线程持续刷新界面,仍需完成传统Android开发的下面几项工作:
    1、声明一个自定义的处理器类Handler,并重写该类的handleMessage方法,根据不同的消息类型进行相应的控件操作;
    2、线程内部针对各种运行状况,调用处理器对象的sendEmptyMessage或者sendMessage方法,发送事先约定好的消息类型;
    举个具体的业务例子,现在有一个新闻版块,每隔两秒在界面上滚动播报新闻,其中便联合运用了线程和处理器,先由线程根据情况发出消息指令,再由处理器按照消息指令轮播新闻。详细的业务代码示例如下:

    class MessageActivity : AppCompatActivity() {
        private var bPlay = false
        private val BEGIN = 0 //开始播放新闻
        private val SCROLL = 1 //持续滚动新闻
        private val END = 2 //结束播放新闻
        private val news = arrayOf("北斗三号卫星发射成功,定位精度媲美GPS", "美国赌城拉斯维加斯发生重大枪击事件", "日本在越南承建的跨海大桥未建完已下沉", "南水北调功在当代,近亿人喝上长江水", "德国外长要求中国尊重“一个欧洲”政策")
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_message)
            tv_message.gravity = Gravity.LEFT or Gravity.BOTTOM
            tv_message.setLines(8)
            tv_message.maxLines = 8
            tv_message.movementMethod = ScrollingMovementMethod()
            btn_start_message.setOnClickListener {
                if (!bPlay) {
                    bPlay = true
                    //线程第一种写法的调用方式,通过具体的线程类进行构造。
                    //注意每个线程实例只能启动一次,不能重复启动。
                    //若要多次执行该线程的任务,则需每次都构造新的线程实例。
                    //PlayThread().start()
                    //线程的第二种写法,采用匿名类的形式。第二种写法无需显式构造
                    Thread {
                        //发送“开始播放新闻”的消息类型
                        handler.sendEmptyMessage(BEGIN)
                        while (bPlay) {
                            //休眠两秒,模拟获取突发新闻的网络延迟
                            Thread.sleep(2000)
                            val message = Message.obtain()
                            message.what = SCROLL
                            message.obj = news[(Math.random() * 30 % 5).toInt()]
                            //发送“持续滚动新闻”的消息类型
                            handler.sendMessage(message)
                        }
                        bPlay = true
                        Thread.sleep(2000)
                        //发送“结束播放新闻”的消息类型
                        handler.sendEmptyMessage(END)
                        bPlay = false
                    }.start()
                }
            }
            btn_stop_message.setOnClickListener { bPlay = false }
        }
    
        //自定义的处理器类,区分三种消息类型,给tv_message显示不同的文本内容
        private val handler = object : Handler() {
            override fun handleMessage(msg: Message) {
                val desc = tv_message.text.toString()
                tv_message.text = when (msg.what) {
                    BEGIN -> "$desc\n${DateUtil.nowTime} 下面开始播放新闻"
                    SCROLL -> "$desc\n${DateUtil.nowTime} ${msg.obj}"
                    else -> "$desc\n${DateUtil.nowTime} 新闻播放结束,谢谢观看"
                }
            }
        }
    
    }

    通过线程加上处理器固然可以实现滚动播放的功能,可是想必大家也看到了,这种交互方式依旧很突兀,还有好几个难以克服的缺点:
    1、自定义的处理器仍然存在类继承和函数重载的冗余写法;
    2、每次操作界面都得经过发送消息、接收消息两道工序,繁琐且拖沓;
    3、线程和处理器均需在指定的Activity代码中声明,无法在别处重用;
    有鉴于此,Android早已提供了异步任务AsyncTask这个模版类,专门用于耗时任务的分线程处理。然而AsyncTask的用法着实不简单,首先它是个模板类,初学者瞅着模板就发慌;其次它区分了好几种运行状态,包括未运行、正在运行、取消运行、运行结束等等,一堆的概念叫人头痛;再次为了各种状况都能与界面交互,又得定义事件监听器及其事件处理方法;末了还得在Activity代码中实现监听器的相应方法,才能正常调用定义好的AsyncTask类。
    初步看了下自定义AsyncTask要做的事情,直让人倒吸一口冷气,看起来很高深的样子,确实每个Android开发者刚接触AsyncTask之时都费了不少脑细胞。为了说明AsyncTask是多么的与众不同,下面来个异步加载书籍任务的完整Java代码,温习一下那些年虐过开发者的AsyncTask:

    //模板类的第一个参数表示外部调用execute方法的输入参数类型,第二个参数表示运行过程中与界面交互的数据类型,第三个参数表示运行结束后返回的输出参数类型
    public class ProgressAsyncTask extends AsyncTask<String, Integer, String> {
        private String mBook;
        //构造函数,初始化数据
        public ProgressAsyncTask(String title) {
            super();
            mBook = title;
        }
    
        //在后台运行的任务代码,注意此处不可与界面交互
        @Override
        protected String doInBackground(String... params) {
            int ratio = 0;
            for (; ratio <= 100; ratio += 5) {
                // 睡眠200毫秒模拟网络通信处理
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //刷新进度,该函数会触发调用onProgressUpdate方法
                publishProgress(ratio);
            }
            return params[0];
        }
    
        //在任务开始前调用,即先于doInBackground执行
        @Override
        protected void onPreExecute() {
            mListener.onBegin(mBook);
        }
    
        //刷新进度时调用,由publishProgress函数触发
        @Override
        protected void onProgressUpdate(Integer... values) {
            mListener.onUpdate(mBook, values[0], 0);
        }
    
        //在任务结束后调用,即后于doInBackground执行
        @Override
        protected void onPostExecute(String result) {
            mListener.onFinish(result);
        }
    
        //在任务取消时调用
        @Override
        protected void onCancelled(String result) {
            mListener.onCancel(result);
        }
    
        //声明监听器对象
        private OnProgressListener mListener;
        public void setOnProgressListener(OnProgressListener listener) {
            mListener = listener;
        }
    
        //定义该任务的事件监听器及其事件处理方法
        public static interface OnProgressListener {
            public abstract void onFinish(String result);
            public abstract void onCancel(String result);
            public abstract void onUpdate(String request, int progress, int sub_progress);
            public abstract void onBegin(String request);
        }}

    见识过了AsyncTask的惊涛骇浪,不禁喟叹开发者的心灵有多么地强大。多线程是如此的令人望而却步,直到Kotlin与Anko的搭档出现,因为它俩在线程方面带来了革命性的思维,即编程理应是面向产品,而非面向机器。对于分线程与界面之间的交互问题,它俩给出了堪称完美的解决方案,所有的线程处理逻辑都被归结为两点:其一是如何标识这种牵涉界面交互的分线程,该点由关键字“doAsync”阐明;其二是如何在分线程中传递消息给主线程,该点由关键字“uiThread”界定。有了这两个关键字,分线程的编码异乎寻常地简单,即使加上Activity的响应代码也只有以下寥寥数行:

        //圆圈进度对话框
        private fun dialogCircle(book: String) {
            dialog = indeterminateProgressDialog("${book}页面加载中……", "稍等")
            doAsync {
                // 睡眠200毫秒模拟网络通信处理
                for (ratio in 0..20) Thread.sleep(200)
                //处理完成,回到主线程在界面上显示书籍加载结果
                uiThread { finishLoad(book) }
            }
        }
    
        private fun finishLoad(book: String) {
            tv_async.text = "您要阅读的《$book》已经加载完毕"
            if (dialog.isShowing) dialog.dismiss()
        }

    以上代码被doAsyc括号圈起来的代码段,就是分线程要执行的全部代码;至于uiThread括号圈起来的代码,则为通知主线程要完成的工作。倘若在分线程运行过程中,要不断刷新当前进度,也只需在待刷新的地方添加一行uiThread便成,下面是添加了进度刷新的代码例子:

        //长条进度对话框
        private fun dialogBar(book: String) {
            dialog = progressDialog("${book}页面加载中……", "稍等")
            doAsync {
                for (ratio in 0..20) {
                    Thread.sleep(200)
                    //处理过程中,实时通知主线程当前的处理进度
                    uiThread { dialog.progress = ratio*100/20 }
                }
                uiThread { finishLoad(book) }
            }
        }

     

     

    点此查看Kotlin入门教程的完整目录


    __________________________________________________________________________
    打开微信扫一扫下面的二维码,或者直接搜索公众号“老欧说安卓”添加关注,更快更方便地阅读技术干货。

     

    展开全文
  • 今天小编就为大家分享一篇PYQT5开启多个线程和窗口,多线程与多窗口的交互实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • Qt中UI线程与子线程交互

    千次阅读 2020-03-29 00:27:48
    0.前言 GUI框架一般只允许UI线程操作界面...1.Qt中几种常见的多线程交互的操作 Qt中提供了一些线程安全的方法来让我们使用: A.使用信号槽 Qt的信号槽是线程安全的。connect函数的第五个参数ConnectionType默...

    0.前言

    GUI框架一般只允许UI线程操作界面组件,Qt也是如此。但我们的应用程序一般是多线程的,势必就涉及到UI线程与子线程的交互。

    下面介绍常用的UI线程与子线程交互方式,并附上自己的Demo。

    1.Qt中几种常见的多线程交互的操作

    Qt中提供了一些线程安全的方法来让我们使用:

    A.使用信号槽

    Qt的信号槽是线程安全的。connect函数的第五个参数ConnectionType默认为Qt::AutoConnection,如果接收者和发送者不在一个线程,则相当于自动使用Qt::QueuedConnection类型,槽函数会在接收者线程执行。

    connect(this,&MainWindow::signalDoing,worker,&MyWorker::slotDoing);

    B.使用 QMetaObject::invokeMethod

    invokeMethod可以线程安全的对目标对象进行操作,如调用目标对象的成员函数等。它也具有一个ConnectionType参数,参照connect。

        qDebug()<<"main thread"<<QThread::currentThread();
        connect(ui->btnDoB,&QPushButton::clicked,this,[this]{
            QtConcurrent::run([=]{
                qDebug()<<"run doing"<<QThread::currentThread();
    
                QMetaObject::invokeMethod(this,[=]{ //这个this就是传递进来的mainwindow
                    qDebug()<<"invoke doing"<<QThread::currentThread();
                    ui->textEditB->append("invoke test");
                });
            });
        });

    C.使用 QApplication::postEvent

    自定义事件我没怎么用,但postEvent也是线程安全的操作。

    QApplication::postEvent(target,new Event(type)));

    当然,要完成多线程中与UI的交互不止上面三种方式,可以参照百度其他人提供的思路。 

    2.示例代码

    运行效果:

    代码git链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/UiAndSubThread

    实现代码长了点,贴主要部分:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QDateTime>
    #include <QtConcurrentRun>
    #include <QDebug>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        setWindowTitle("QQ交流群:647637553");
    
        //【示例A】通过信号槽
        thread=new QThread(this);
        //要move到别的线程的话不要传递parent参数
        worker=new MyWorker();
        //更改该对象及其子对象的线程关联性。如果对象具有父对象,则无法移动。
        //事件处理将在targetThread中继续。
        worker->moveToThread(thread);
        //官方示例里的释放方式
        connect(thread,&QThread::finished,worker,&QObject::deleteLater);
        //worker的定时器开关
        ui->btnTimer->setCheckable(true); //Checkable就能有两种状态-对应定时器开关
        connect(ui->btnTimer,&QPushButton::clicked,worker,&MyWorker::slotTimerSwitch);
        //worker执行任务
        connect(ui->btnDoA,&QPushButton::clicked,this,[this]{
            emit signalDoing(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")+" main");
        });
        connect(this,&MainWindow::signalDoing,worker,&MyWorker::slotDoing);
        //worker操作结果
        connect(worker,&MyWorker::signalMessage,ui->textEditA,&QTextEdit::append);
        //启动线程
        thread->start();
    
        //【示例B】通过invokeMethod方法
        //(这里我直接用concurrent模块的run函数)
        qDebug()<<"main thread"<<QThread::currentThread();
        connect(ui->btnDoB,&QPushButton::clicked,this,[this]{
            QtConcurrent::run([=]{
                qDebug()<<"run doing"<<QThread::currentThread();
                //使用QMetaObject::invokeMethod()操作是线程安全的
                QMetaObject::invokeMethod(this,[=]{ //这个this就是传递进来的mainwindow
                    qDebug()<<"invoke doing"<<QThread::currentThread();
                    ui->textEditB->append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")+" invoke finished");
                });
            });
        });
    
        //【示例C】通过postEvent
        //(需要重写接收者的event()事件处理函数)
        connect(ui->btnDoC,&QPushButton::clicked,this,[this]{
            QtConcurrent::run([=]{
                qDebug()<<"run doing"<<QThread::currentThread();
                //postEvent是非阻塞的,sendEvent是阻塞的,postEvent是线程安全的
                QApplication::postEvent(this,new MyEvent(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")));
            });
        });
    }
    
    MainWindow::~MainWindow()
    {
        //退出示例A的线程,注意里面若有死循环要提前break
        thread->quit();
        thread->wait();
        delete ui;
    }
    
    bool MainWindow::event(QEvent *event)
    {
        if(event->type()==MyEvent::eventType){
            MyEvent *my_event=dynamic_cast<MyEvent*>(event);
            if(my_event){
                //通过postevent传递信息
                ui->textEditB->append(my_event->message+" event finished");
            }
        }
    
        return QMainWindow::event(event);
    }
    
    

    3.参考

    参考文档:https://doc.qt.io/qt-5.12/qthread.html

    参考博客:https://www.cnblogs.com/lifexy/p/10907901.html

    展开全文
  • 多线程对爬虫的效率提高是非凡的,当我们使用...幸运的是,爬虫大部分时间在网络交互上,所以可以使用多线程来编写爬虫。3.这点其实和多线程关系不大,scrapy的并发并不是采用多线程来实现,它是一个twisted应用,通过异
  • 多线程使用进度条+(实例+C++代码)MFC系列
  • Labview2019,通讯三菱plc,用opc和mc通讯协议,sqlite数据库,labview标准框架jki状态机,加上数组队列,实现多线程交互。 _:4550670508310561加菲猫眼

    Labview2019,通讯三菱plc,用opc和mc通讯协议,sqlite数据库,labview标准框架jki状态机,加上数组队列,实现多线程交互。

    请添加图片描述_:4550670508310561加菲猫眼

    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述

    展开全文
  • C#多线程编程实例 线程与窗体交互

    万次阅读 多人点赞 2014-07-10 22:49:16
    C#多线程编程实例 线程与窗体交互代码: public partial class Form1 : Form { //声明线程数组 Thread[] workThreads = new Thread[10]; public Form1() { InitializeComponent();
  • 易语言多线程多任务下载软件源码,多线程多任务下载软件,开始监听邮槽,分析命令行,返回命令行,填充显示下载完毕类标框,建立新成员,保存正在下载,取文件图标a,交互回调函数,停止更新,开始更新,网址取文件名,取文件名,...
  • 【python】多线程详解

    千次阅读 2021-01-29 18:22:57
    二、线程基础1、线程的状态线程有5种状态,状态转换的过程如下图所示:2、线程同步(锁)多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。考虑这样...
  • C#多线程编程实例 线程与窗体交互源码.rar
  • 线程间交换数据python

    2021-01-12 02:23:54
    player1, "x_player2" : x_player2 } s=json.dumps(data) return s while True: conn, addr = server.accept() start_new_thread(threaded_client,(conn,)) 当客户端连接程序时,它会创建一个新线程。唯一的问题是,...
  • Java多线程——交替输出

    千次阅读 2021-01-17 22:12:58
    设置两个线程交替输出"1 2 1 2 1 2…" 1. 定义一个全局锁对象 为了保证两个线程使用的一定是同一个锁,我们创建一个对象作为静态属性放到一个类中, 这个对象就用来充当锁 2. 定义一个线程输出1,另外一个线程输出2 ...
  • 线程的创建及应用 HANDLE hand = CreateThread(0,0,ThreadFunc,NULL,0,NULL); static DWORD WINAPI ThreadFun(LPVOID p) { //to do :具体实现过程 return 0; } 函数原型: HANDLE CreateThread ( LPSE...
  • android UI线程和后台线程交互,包括多线程之AsyncTask等例子展示。。。。。。。。。。。
  • python的多线程及线程间的通信方式

    千次阅读 2020-11-04 22:56:15
    而一个进程中有可以同时拥有多个线程执行,这种情况就被称为多线程。 并发与并行: 线程的并发是指处理器CPU在极短的时间内交替执行每个线程,而并行是指CPU同时处理多个线程。 2. 创建线程的方法 在python中,创建...
  • c#多线程处理

    2016-07-14 22:15:15
    c# 多线程 task Thread
  • C++ 多线程之间的数据交互

    千次阅读 2021-01-14 14:36:00
    同一个进程内的线程之间免不了需要进行数据的交互,队列和共享数据是实现线程之间的数据交互的常用方式,封装好的队列使用起来相对来说不容易出错一些,而共享数据则是最基本的也是较容易出错的,因为它会...
  • 第七部分 多线程使用和线程中UI交互 7.1 Thread类和Runnable接口 对于线程android的帮助如下描述 There are two ways to execute code in a new thread. You can either subclass Thread and overriding its run) ...
  • 软件核心采用多线程技术,将数据采集、试验控制、UI交互以及安全监测等功能模块放在不同线程内执行。线程同步技术保证了线程之间对象高速传输的准确性。该软件设计方式不仅大幅度提高了软件的稳定性和可靠性,还充分...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 367,910
精华内容 147,164
关键字:

多线程如何交互