精华内容
下载资源
问答
  • Qt创建多线程接收惯导UDP数据

    千次阅读 2018-01-04 16:31:09
    项目需求,要用Qt接收惯导数据,数据采用UDP传输,在Qt中提供了QUdpSocket类来进行UDP数据报(datagrams)的发送和接收。这里我们还要了解一个名词Socket,也就是常说的“套接字”。 Socket简单地说,就是一个IP地址...

    0 背景

    项目需求,要用Qt接收惯导数据,数据采用UDP传输,在Qt中提供了QUdpSocket类来进行UDP数据报(datagrams)的发送和接收。这里我们还要了解一个名词Socket,也就是常说的“套接字”。 Socket简单地说,就是一个IP地址加一个port端口。因为我们要传输数据,就要知道往哪个机子上传送,而IP地址确定了一台主机,但是这台机子上可能运行着各种各样的网络程序,我们要往哪个程序中发送呢?这时就要使用一个端口来指定UDP程序。所以说,Socket指明了数据报传输的路径。

    为了使惯导数据的接收不影响主页面的响应,创建新线程来不停地读取数据。下面创建一个UDP的接收端程序。新建一个mainwindow工程,在pro文件中添加QT += network,然后执行以下qmake,确保没有问题。新建gnss类,继承QThread,包括gnss.h和gnss.cpp文件。话不多说,直接上代码!

    1 gnss.h

    #ifndef GNSS_H
    #define GNSS_H
    #include <QThread>
    #include <QtCore>
    #include <QObject>
    #include <QMutex>
    #include <iostream>
    #include <QtNetwork>
    
    class GNSS: public QThread
    {
    public:    
        Q_OBJECT
    public:
        GNSS();
        void stop();
    private slots:
        void run();
        void processPendingDatagram();
    private:
        bool stopped;
        QMutex m_lock;
        QUdpSocket *receiver;
        QFile f();
    };
    
    #endif // GNSS_H

    2 gnss.cpp

    #include "gnss.h"
    #include <QTimer>
    #include <QMutexLocker>
    #include "mainwindow.h"
    
    GNSS::GNSS()
    {
    
        stopped = false;//标记可以运行
        //创建一个QUdpSocket类对象,该类提供了Udp的许多相关操作
        receiver = new QUdpSocket(this);
        int port =3000;//设置UDP的端口号参数,指定在此端口上监听数据
        //此处的bind是个重载函数,连接本机的port端口,采用ShareAddress模式(即允许其它的服务连接到相同的地址和端口,特别是
        //用在多客户端监听同一个服务器端口等时特别有效),和ReuseAddressHint模式(重新连接服务器)
        int receive = receiver->bind(QHostAddress("195.0.0.230"),port);
        qDebug() << "receive: " <<receive << endl;
        if(receive == 0)
        {
            qDebug() << "UDP Connected Succeed ! " << endl;
        }
        else
        {
            qDebug() << "UDP Connected Faild ! " << endl;
        }
    }
    void GNSS::stop()
    {
        QMutexLocker locker(&m_lock);
        stopped = true;
        receiver->close();
        delete receiver;
    
    }
    
    void GNSS::run()
    {
        //获得系统时间并输出
        QString min = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
        //打开文本 以时间命名文件名字
        QString fileName = "C:\\SensorsData\\BASLER\\code\\build-opencv-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug\\GNSS_" + min + ".txt";//假设指定文件夹路径为D盘根目录
        QFile f(fileName);
        LONGLONG time_now=0;
        QString s = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
        QString a = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
        QString b = QDateTime::currentDateTime().toString("mmss");
    
        while(!stopped)
        {
            while(receiver->hasPendingDatagrams())  //拥有等待的数据报
            {
               //qDebug() << "receive succeed ! " << endl;
                QByteArray datagram; //拥于存放接收的数据报
               //pendingDatagramSize为返回第一个在等待读取报文的size,
               //resize函数是把datagram的size归一化到参数size的大小一样
               datagram.resize(receiver->pendingDatagramSize());
               //接收数据报,将其存放到datagram中
               //将读取到的不大于datagram.size()大小数据输入到datagram.data()中,
               //datagram.data()返回的是一个字节数组中存储数据位置的指针
               receiver->readDatagram(datagram.data(),datagram.size());
               //将数据报内容显示出来
               QString HexData = datagram.toHex();
               //判断数据是否完整
               if(HexData.length() == 144)
               {
                   //解析Hex数据
                   QString Status = HexData.mid(42,2);
                   qDebug() << "Status :" << Status  << endl;
                   QString Latitude = HexData.mid(46,16);
                   qDebug() << "Latitude :" << Latitude  << endl;
                   QString Longitude = HexData.mid(62,16);
                   qDebug() << "Longitude :" << Longitude  << endl;
                   QString Altitude = HexData.mid(78,8);
                   qDebug() << "Altitude :" << Altitude  << endl;
                   QString North_Velocity = HexData.mid(86,6);
                   qDebug() << "North_Velocity :" << North_Velocity  << endl;
                   QString East_Velocity = HexData.mid(92,6);
                   qDebug() << "East_Velocity :" << East_Velocity  << endl;
                   QString Down_Velocity = HexData.mid(98,6);
                   qDebug() << "Down_Velocity :" << Down_Velocity  << endl;
                   QString Heading = HexData.mid(104,6);
                   qDebug() << "Heading :" << Heading  << endl;
                   QString Pitch = HexData.mid(110,6);
                   qDebug() << "Pitch :" << Pitch  << endl;
                   QString Roll = HexData.mid(116,6);
                   qDebug() << "Roll :" << Roll  << endl;
    
                   //获得系统时间并输出
                   s = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
                   a = QDateTime::currentDateTime().toString("yyyyMMddhhmm");
                   b = QDateTime::currentDateTime().toString("mmss");
                   qDebug() << "min : " <<min<< endl;
                   time_now = b.toLongLong();
                   qDebug() << "time_now: " <<time_now<< endl;
                   //每10分钟保存一次
                   if(time_now % 1000 == 0)
                   {
                       qDebug() << "write again ! " <<time_now<< endl;
                       f.fileName() = "C:\\SensorsData\\BASLER\\code\\build-opencv-Desktop_Qt_5_8_0_MSVC2015_64bit-Debug\\GNSS_" + a + ".txt";
                   }
    
                   qDebug() << "time" << s;
    
                   if(!f.open( QIODevice::Append))
                   {
                       cout << "Open failed." << endl;
                   }
                   QTextStream txtOutput(&f);
                   txtOutput << s <<"$$"<< HexData<< "&&" << endl;
    
                   f.close();
    
               }
               else
               {
                   qDebug() << "The data is not complete !" << endl;
               }
            }
        }
    
    
    }
    /***********GNSS数据处理***********/
    void GNSS::processPendingDatagram()
    {
    
    }
    

    3 mainwindow.cpp

    在ui界面新建两个button,分别是开始接收和停止接收,分别命名为StartGNSSBtn和StopGNSSBtn,然后在mainwindow.cpp中添加对应的槽函数

    /***********开始GNSS数据接收***********/
    void MainWindow::on_StartGNSSBtn_clicked()
    {
        if(!gnss.isRunning())
        {
            gnss.start();
            ui->StartGNSSBtn->setEnabled(false);
            ui->StopGNSSBtn->setEnabled(true);
        }
        else
        {
            qDebug() << "GNSS receive has started !!!" << endl;
    
        }
    
    }
    
    /***********停止GNSS数据接收***********/
    void MainWindow::on_StopGNSSBtn_clicked()
    {
        if(gnss.isRunning())
        {
            gnss.stop();
        }
        ui->StartGNSSBtn->setEnabled(true);
        ui->StopGNSSBtn->setEnabled(false);
    }

    同时要记得在mainwindow.h中添加相关的变量和函数,添加头文件#include "gnss.h"

    private:
        //GNSS相关对象
        GNSS gnss;
        QPushButton *StartGNSSBtn;
        QPushButton *StopGNSSBtn;
    private slots:
        //GNSS相关的槽函数
        void on_StartGNSSBtn_clicked();
        void on_StopGNSSBtn_clicked();

    至此完成在新的线程里接收GNSS数据并初步的分割保存工作,下一步进行解析及显示

    展开全文
  • python使用pyqt多线程显示视频

    千次阅读 2020-04-14 16:05:27
    在正式开始之前,得说几句感想,在...在这个项目中,我使用了多线程来在界面上显示视频,今天想了想,还是写上这个,用于以后自己看或者给其他有兴趣的朋友看。 使用的技术:python + pyqt + opencv 在使用pyqt的...

    在正式开始之前,得说几句感想,在最初学python的时候,对于多线程多进程,只有一个概念以及知道大概怎么用,但是什么情况下使用多线程,什么情况下使用多进程,我对这个概念还不是很清晰,正好前一个月左右做了个项目,在这个项目中,我使用了多线程来在界面上显示视频,今天想了想,还是写上这个,用于以后自己看或者给其他有兴趣的朋友看。

    使用的技术:python + pyqt + opencv

    在使用pyqt的过程中,我们经常就会遇到一种情况,当我们在界面上要显示视频的时候,会出现卡死的情况,要解决这个问题,就得用到多线程的技术,利用分出去的线程来处理后台发过来的图像数据,然后线程将这个图像进行显示。

    首先,在Pyqt中,我们如何利用designed来设计整个软件的模样,如何让这个软件的界面好看,这个不属于我们这篇文章的讨论内容,我们值讨论如何让界面正常的显示视频并且不会卡顿。

    为了解决这个问题,我们可以得了解pyqt的信号机制这些,这些也不属于我这篇文章讨论的内容,有不明白的可以自行了解,在这个问题中,我们要解决这个问题,得构建一个类,这个类继承pyqt中的QThread,代码如下:

    '''this code is designed by nike hu'''
    class imageThread(QThread):
        showImage = pyqtSignal(object) # 这里可以不要,这里是定义信号的
        def __init__(self, image): # 在调用这个类的时候传入image这个参数
            super().__init__()
            self.image = image
    
        def run(self): # 在使用这个类的时候直接调用这个接口就行
            # self.showImage.emit(image_color)
            image = self.image
            (imageH, imageW) = image.shape[0:2]
            Qimag = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # pyqt中正常显示图片需要将opencv中的bgr转化为rgb
            QiM = QImage(Qimag.data, imageW, imageH,
                         imageW * 3, QImage.Format_RGB888) # 这也是将图像显示在界面中的必备步骤,QImage是从PyQt5.QtGui中导入的
            ui.hotImage.setPixmap(QPixmap.fromImage(QiM)) # ui.hotImage是pyqt中的lable控件,这个名字是你在画界面的时候自己定义的
    
    

    在处理这类问题的时候,我的处理方法一般是把整个界面当成一个主线程,然后我会另外开一个线程来处理后台的数据,我们将这个线程称为子线程1,那么子线程1在处理接收到的每一帧图像数据的时候,我要么在子线程1的基础上另外开一个子线程2来处理图像的线程,子线程2将图像显示完就自己结束自己,或者我在主线程上开两个线程,一个子线程1,一个子线程2,子线程2是一个while循环,子线程1负责处理后台数据,然后通过全局变量的方式,子线程2来读取子线程1处理的图像数据,经过我的实验,最后界面是不再存在卡顿的现象。由于我的那个涉及到显示视频的是一个完整的项目,要将里面的代码拆分出来显示,不好拆分,所以就不再贴代码了,思路我已经提供了。对了,注意视频流图像读取的速度以及界面显示图像的速度,如果是毫无间隙的进行操作,也可能会出现卡顿现象,这时候,使用time.sleep(0.01)让这个线程休息0.01s或者再稍微长一点的时间,那么就可以避免这个问题的产生。

    总结一下:多线程适用这种i/o延时高的操作,如果后台涉及到大量的数据运算的情况,那么就必须得利用多进程了,我有空会写一篇来处理人脸识别中导致的视频显示的效果帧数太低的情况。

    2020 4.14

    展开全文
  • } } } 5、数据接收 /// /// 后台线程接收数据 /// /// private void ReceiveClient(object obj) { Socket _ClientSocket = (Socket)obj; while (true) { try { //获取数据长度 int receiveLength ...

    1、引入依赖库,无需第三方。

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;
    

    2、变量声明

           /// <summary>
            /// 缓冲器
            /// </summary>
    private byte[] result = new byte[1024];
            /// <summary>
            /// 最大连接数
            /// </summary>
    private int maxClientCount;
            /// <summary>
            /// 服务IP地址
            /// </summary>
    private string ip;
            /// <summary>
            /// 服务端口号
            /// </summary>
    private int port;
            // 编码
            // private string code;
            /// <summary>
            /// 客户端列表
            /// </summary>
    private List<Socket> ClientSockets;
            /// <summary>
            /// IP终端
            /// </summary>
    private IPEndPoint ipEndPoint;
            /// <summary>
            /// 服务端Socket 
            /// </summary>
    private Socket ServerSocket;
            /// <summary>
            /// 当前客户端Socket 
            /// </summary>
    private Socket ClientSocket;
    

    3、开启TCP服务

    ip = IPAddress.Any.ToString(); //服务于任意IP地址      
    port=8640;//服务端口,理论自定义范围 1-65535,不可与以开放端口冲突
    maxClientCount = 256; //设定最大连接数
    ClientSockets = new List<Socket>();
    ipEndPoint = new IPEndPoint(IPAddress.Any, port);      //初始化IP终端 
    ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);        //初始化服务端Socket 
    ServerSocket.Bind(this.ipEndPoint);      //端口绑定
    ServerSocket.Listen(maxClientCount);     //设置监听数目 
    Thread ServerThread = new Thread(() =>
    {
         ListenClientConnect();         //开启监听,后台线程,非阻塞
    })
    {
         IsBackground = true,
    };
    ServerThread.Start();//服务端线程开启 
    

    4、TCP连接侦听

    /// <summary>
    /// 后台线程侦听连接
    /// </summary>
    private void ListenClientConnect()
    {
         //设置循环标志位 
    	while (true)
    	{
    		try
    		{
    			//获取连接到服务端的客户端 
                ClientSocket = ServerSocket.Accept();
    			//将获取到的客户端添加到客户端列表 
    			ClientSockets.Add(ClientSocket);
    			//显示连接者信息
    			//Show_msg($@"客户端接入:{ClientSocket.RemoteEndPoint}");
    			//创建客户端消息线程,实现客户端消息的循环监听 
                Thread ReveiveThread = new Thread(() =>
    		    {
    				ReceiveClient(ClientSocket); //数据接收
                })
                {
                    IsBackground = true,
                };
                ReveiveThread.Start();
             }
             catch (Exception ex)
             {
                //Show_msg($@"连接侦听异常:{ex.Message}");
    		 }
    	}
    }
    

    5、数据接收

    /// <summary>
    /// 后台线程接收数据
    /// </summary>
    /// <param name="obj"></param>
    private void ReceiveClient(object obj)
    {
    	Socket _ClientSocket = (Socket)obj;
    	while (true)
    	{
    		try
    		{
    			//获取数据长度 
    			int receiveLength = _ClientSocket.Receive(result);
    			//获取客户端发来的数据 
    			string strData = Encoding.UTF8.GetString(result, 0, receiveLength);
                //Show_msg($@"客户端{_ClientSocket.RemoteEndPoint}】  发来数据:{strData}"); 
                /*
                	对接收到的数据进行处理
                */
                //向客户端回发数据
    			//_ClientSocket.Send(Encoding.UTF8.GetBytes($@"我已收到您发来的数据{strData }"));
    		}
    		catch (Exception e)//通讯出现异常 
    		{
    			//从客户端列表中移除该客户端 
    			this.ClientSockets.Remove(_ClientSocket);
                //断开连接 
    			//Show_msg($@"客户端{_ClientSocket.RemoteEndPoint}从服务器断开,断开原因:{e.Message}");
    			_ClientSocket.Shutdown(SocketShutdown.Both);
    			_ClientSocket.Close();
    			break;
    		}
    	}
    }
    
    展开全文
  • 我是在QT中写的客户端,与linux中写的服务器连接,连通时自动在lcdnum上面分别显示温度和湿度。网上的例子也看了很,但是还是一直出错,具体代码如下,有大牛给个思路吗? #ifndef MYTHREAD_H #define MYTHREAD_H...
  • 在主线程开启子线程初始化,读写串口数据,有数据就读,提取有效数据,并用信号将数据传送给主线程,创建界面对象,主线程通过开关按钮槽函数控制串口的打开关闭,图形的显示以及暂停,实时刷新波形。串口读取数据...
  • Java多线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 线程的相关API //获取当前...

    引言

    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。
    那么话不多说,今天本帅将记录自己线程的学习。

    程序,进程,线程的基本概念+并行与并发:

    程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
    进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期
    线程:进程可进一步细化为线程,是一个程序内部的一条执行路径

    即:线程《线程(一个程序可以有多个线程)
    程序:静态的代码 进程:动态执行的程序
    线程:进程中要同时干几件事时,每一件事的执行路径成为线程。

    并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事
    并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事

    线程的相关API

    //获取当前线程的名字
    Thread.currentThread().getName()

    1.start():1.启动当前线程2.调用线程中的run方法
    2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    3.currentThread():静态方法,返回执行当前代码的线程
    4.getName():获取当前线程的名字
    5.setName():设置当前线程的名字
    6.yield():主动释放当前线程的执行权
    7.join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去
    8.stop():过时方法。当执行此方法时,强制结束当前线程。
    9.sleep(long millitime):线程休眠一段时间
    10.isAlive():判断当前线程是否存活

    判断是否是多线程

    一条线程即为一条执行路径,即当能用一条路径画出来时即为一个线程
    例:如下看似既执行了方法一,又执行了方法2,但是其实质就是主线程在执行方法2和方法1这一条路径,所以就是一个线程

    public class Sample{
    		public void method1(String str){
    			System.out.println(str);
    		}
    	
    	public void method2(String str){
    		method1(str);
    	}
    	
    	public static void main(String[] args){
    		Sample s = new Sample();
    		s.method2("hello");
    	}
    }
    

    在这里插入图片描述

    线程的调度

    调度策略:
    时间片:线程的调度采用时间片轮转的方式
    抢占式:高优先级的线程抢占CPU
    Java的调度方法:
    1.对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略
    2.对高优先级,使用优先调度的抢占式策略

    线程的优先级

    等级:
    MAX_PRIORITY:10
    MIN_PRIORITY:1
    NORM_PRIORITY:5

    方法:
    getPriority():返回线程优先级
    setPriority(int newPriority):改变线程的优先级

    注意!:高优先级的线程要抢占低优先级的线程的cpu的执行权。但是仅是从概率上来说的,高优先级的线程更有可能被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。

    多线程的创建方式

    1. 方式1:继承于Thread类

    1.创建一个集成于Thread类的子类 (通过ctrl+o(override)输入run查找run方法)
    2.重写Thread类的run()方法
    3.创建Thread子类的对象
    4.通过此对象调用start()方法

    start与run方法的区别:

    start方法的作用:1.启动当前线程 2.调用当前线程的重写的run方法(在主线程中生成子线程,有两条线程)
    调用start方法以后,一条路径代表一个线程,同时执行两线程时,因为时间片的轮换,所以执行过程随机分配,且一个线程对象只能调用一次start方法。
    run方法的作用:在主线程中调用以后,直接在主线程一条线程中执行了该线程中run的方法。(调用线程中的run方法,只调用run方法,并不新开线程)

    总结:我们不能通过run方法来新开一个线程,只能调用线程中重写的run方法(可以在线程中不断的调用run方法,但是不能开启子线程,即不能同时干几件事),start是开启线程,再调用方法(即默认开启一次线程,调用一次run方法,可以同时执行几件事)
    在这里插入图片描述

    多线程例子(火车站多窗口卖票问题)

    	package com.example.paoduantui.Thread;
    	
    	import android.view.Window;
    	
    	/**
    	 *
    	 * 创建三个窗口卖票,总票数为100张,使用继承自Thread方式
    	 * 用静态变量保证三个线程的数据独一份
    	 * 
    	 * 存在线程的安全问题,有待解决
    	 *
    	 * */
    	
    	public class ThreadDemo extends Thread{
    	
    	    public static void main(String[] args){
    	        window t1 = new window();
    	        window t2 = new window();
    	        window t3 = new window();
    	
    	        t1.setName("售票口1");
    	        t2.setName("售票口2");
    	        t3.setName("售票口3");
    	
    	        t1.start();
    	        t2.start();
    	        t3.start();
    	    }
    	
    	}
    	
    	class window extends Thread{
    	    private static int ticket = 100; //将其加载在类的静态区,所有线程共享该静态变量
    	
    	    @Override
    	    public void run() {
    	        while(true){
    	            if(ticket>0){
    	//                try {
    	//                    sleep(100);
    	//                } catch (InterruptedException e) {
    	//                    e.printStackTrace();
    	//                }
    	                System.out.println(getName()+"当前售出第"+ticket+"张票");
    	                ticket--;
    	            }else{
    	                break;
    	            }
    	        }
    	    }
    	}
    

    2. 方式2:实现Runable接口方式

    1.创建一个实现了Runable接口的类
    2.实现类去实现Runnable中的抽象方法:run()
    3.创建实现类的对象
    4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
    5.通过Thread类的对象调用start()

    具体操作,将一个类实现Runable接口,(插上接口一端)。
    另外一端,通过实现类的对象与线程对象通过此Runable接口插上接口实现

    	package com.example.paoduantui.Thread;
    	
    	public class ThreadDemo01 {
    	    
    	    public static  void main(String[] args){
    	        window1 w = new window1();
    	        
    	        //虽然有三个线程,但是只有一个窗口类实现的Runnable方法,由于三个线程共用一个window对象,所以自动共用100张票
    	        
    	        Thread t1=new Thread(w);
    	        Thread t2=new Thread(w);
    	        Thread t3=new Thread(w);
    	
    	        t1.setName("窗口1");
    	        t2.setName("窗口2");
    	        t3.setName("窗口3");
    	        
    	        t1.start();
    	        t2.start();
    	        t3.start();
    	    }
    	}
    	
    	class window1 implements Runnable{
    	    
    	    private int ticket = 100;
    	
    	    @Override
    	    public void run() {
    	        while(true){
    	            if(ticket>0){
    	//                try {
    	//                    sleep(100);
    	//                } catch (InterruptedException e) {
    	//                    e.printStackTrace();
    	//                }
    	                System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票");
    	                ticket--;
    	            }else{
    	                break;
    	            }
    	        }
    	    }
    	}
    

    比较创建线程的两种方式:
    开发中,优先选择实现Runable接口的方式
    原因1:实现的方式没有类的单继承性的局限性
    2:实现的方式更适合用来处理多个线程有共享数据的情况
    联系:Thread也是实现自Runable,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中

    3.新增的两种创建多线程方式

    1.实现callable接口方式:

    与使用runnable方式相比,callable功能更强大些:
    runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值
    方法可以抛出异常
    支持泛型的返回值
    需要借助FutureTask类,比如获取返回结果

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * 创建线程的方式三:实现callable接口。---JDK 5.0新增
     *是否多线程?否,就一个线程
     *
     * 比runable多一个FutureTask类,用来接收call方法的返回值。
     * 适用于需要从线程中接收返回值的形式
     * 
     * //callable实现新建线程的步骤:
     * 1.创建一个实现callable的实现类
     * 2.实现call方法,将此线程需要执行的操作声明在call()中
     * 3.创建callable实现类的对象
     * 4.将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象
     * 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值)
     * 
     * */
    
    
    //实现callable接口的call方法
    class NumThread implements Callable{
    
        private int sum=0;//
    
        //可以抛出异常
        @Override
        public Object call() throws Exception {
            for(int i = 0;i<=100;i++){
                if(i % 2 == 0){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                    sum += i;
                }
            }
            return sum;
        }
    }
    
    public class ThreadNew {
    
        public static void main(String[] args){
            //new一个实现callable接口的对象
            NumThread numThread = new NumThread();
    
            //通过futureTask对象的get方法来接收futureTask的值
            FutureTask futureTask = new FutureTask(numThread);
    
            Thread t1 = new Thread(futureTask);
            t1.setName("线程1");
            t1.start();
    
            try {
                //get返回值即为FutureTask构造器参数callable实现类重写的call的返回值
               Object sum = futureTask.get();
               System.out.println(Thread.currentThread().getName()+":"+sum);
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    使用线程池的方式:

    背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
    思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。(数据库连接池)
    好处:提高响应速度(减少了创建新线程的时间)
    降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    便于线程管理
    corePoolSize:核心池的大小
    maximumPoolSize:最大线程数
    keepAliveTime:线程没有任务时最多保持多长时间后会终止
    。。。。。。

    JDK 5.0 起提供了线程池相关API:ExecutorService 和 Executors
    ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
    void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
    Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable
    void shutdown():关闭连接池。

    Executors 工具类,线程池的工厂类,用于创建并返回不同类型的线程池
    Executors.newCachedThreadPool() 创建一个可根据需要创建新线程的线程池
    Executors.newFixedThreadPool(n) 创建一个可重用固定线程数的线程池
    Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
    Executors.newScheduledThreadPool(n) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

    线程池构造批量线程代码如下:

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 创建线程的方式四:使用线程池(批量使用线程)
     *1.需要创建实现runnable或者callable接口方式的对象
     * 2.创建executorservice线程池
     * 3.将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
     * 4.关闭线程池
     *
     * */
    
    class NumberThread implements Runnable{
    
    
        @Override
        public void run() {
            for(int i = 0;i<=100;i++){
                if (i % 2 ==0 )
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    
    class NumberThread1 implements Runnable{
        @Override
        public void run() {
            for(int i = 0;i<100; i++){
                if(i%2==1){
                    System.out.println(Thread.currentThread().getName()+":"+i);
                }
            }
        }
    }
    
    public class ThreadPool {
    
        public static void main(String[] args){
    
            //创建固定线程个数为十个的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(10);
    
            //new一个Runnable接口的对象
            NumberThread number = new NumberThread();
            NumberThread1 number1 = new NumberThread1();
    
            //执行线程,最多十个
            executorService.execute(number1);
            executorService.execute(number);//适合适用于Runnable
    
            //executorService.submit();//适合使用于Callable
            //关闭线程池
            executorService.shutdown();
        }
    
    }
    

    目前两种方式要想调用新线程,都需要用到Thread中的start方法。

    java virtual machine(JVM):java虚拟机内存结构

    程序(一段静态的代码)——————》加载到内存中——————》进程(加载到内存中的代码,动态的程序)
    进程可细分为多个线程,一个线程代表一个程序内部的一条执行路径
    每个线程有其独立的程序计数器(PC,指导着程序向下执行)与运行栈(本地变量等,本地方法等)
    在这里插入图片描述

    大佬传送门:https://blog.csdn.net/bluetjs/article/details/52874852

    线程通信方法:

    wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。

    由于wait,notify,以及notifyAll都涉及到与锁相关的操作
    wait(在进入锁住的区域以后阻塞等待,释放锁让别的线程先进来操作)---- Obj.wait 进入Obj这个锁住的区域的线程把锁交出来原地等待通知
    notify(由于有很多锁住的区域,所以需要将区域用锁来标识,也涉及到锁) ----- Obj.notify 新线程进入Obj这个区域进行操作并唤醒wait的线程

    有点类似于我要拉粑粑,我先进了厕所关了门,但是发现厕所有牌子写着不能用,于是我把厕所锁给了别人,别人进来拉粑粑还是修厕所不得而知,直到有人通知我厕所好了我再接着用。

    所以wait,notify需要使用在有锁的地方,也就是需要用synchronize关键字来标识的区域,即使用在同步代码块或者同步方法中,且为了保证wait和notify的区域是同一个锁住的区域,需要用锁来标识,也就是锁要相同的对象来充当

    线程的分类:

    java中的线程分为两类:1.守护线程(如垃圾回收线程,异常处理线程),2.用户线程(如主线程)

    若JVM中都是守护线程,当前JVM将退出。(形象理解,唇亡齿寒)

    线程的生命周期:

    JDK中用Thread.State类定义了线程的几种状态,如下:

    线程生命周期的阶段 描述
    新建 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
    就绪 处于新建状态的线程被start后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
    运行 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能
    阻塞 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
    死亡 线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

    在这里插入图片描述

    线程的同步:在同步代码块中,只能存在一个线程。

    线程的安全问题:

    什么是线程安全问题呢?
    线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。

    上述例子中:创建三个窗口卖票,总票数为100张票
    1.卖票过程中,出现了重票(票被反复的卖出,ticket未被减少时就打印出了)错票。
    2.问题出现的原因:当某个线程操作车票的过程中,尚未完成操作时,其他线程参与进来,也来操作车票。(将此过程的代码看作一个区域,当有线程进去时,装锁,不让别的线程进去)
    生动理解的例子:有一个厕所,有人进去了,但是没有上锁,于是别人不知道你进去了,别人也进去了对厕所也使用造成错误。
    3.如何解决:当一个线程在操作ticket时,其他线程不能参与进来,直到此线程的生命周期结束
    4.在java中,我们通过同步机制,来解决线程的安全问题。

    方式一:同步代码块
    使用同步监视器(锁)
    Synchronized(同步监视器){
    //需要被同步的代码
    }
    说明:

    1. 操作共享数据的代码(所有线程共享的数据的操作的代码)(视作卫生间区域(所有人共享的厕所)),即为需要共享的代码(同步代码块,在同步代码块中,相当于是一个单线程,效率低)
    2. 共享数据:多个线程共同操作的数据,比如公共厕所就类比共享数据
    3. 同步监视器(俗称:锁):任何一个的对象都可以充当锁。(但是为了可读性一般设置英文成lock)当锁住以后只能有一个线程能进去(要求:多个线程必须要共用同一把锁,比如火车上的厕所,同一个标志表示有人)

    Runable天生共享锁,而Thread中需要用static对象或者this关键字或者当前类(window。class)来充当唯一锁

    方式二:同步方法
    使用同步方法,对方法进行synchronized关键字修饰
    将同步代码块提取出来成为一个方法,用synchronized关键字修饰此方法。
    对于runnable接口实现多线程,只需要将同步方法用synchronized修饰
    而对于继承自Thread方式,需要将同步方法用static和synchronized修饰,因为对象不唯一(锁不唯一)

    总结:1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
    2.非静态的同步方法,同步监视器是this
    静态的同步方法,同步监视器是当前类本身。继承自Thread。class

    方式三:JDK5.0新增的lock锁方法

    package com.example.paoduantui.Thread;
    
    
    import java.util.concurrent.locks.ReentrantLock;
    
    class Window implements Runnable{
        private int ticket = 100;//定义一百张票
        //1.实例化锁
        private ReentrantLock lock = new ReentrantLock();
    
        @Override
        public void run() {
            
                while (true) {
    
                    //2.调用锁定方法lock
                    lock.lock();
    
                    if (ticket > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        System.out.println(Thread.currentThread().getName() + "售出第" + ticket + "张票");
                        ticket--;
                    } else {
                        break;
                    }
                }
    
    
            }
    }
    
    public class LockTest {
    
        public static void main(String[] args){
           Window w= new Window();
    
           Thread t1 = new Thread(w);
           Thread t2 = new Thread(w);
           Thread t3 = new Thread(w);
    
           t1.setName("窗口1");
           t2.setName("窗口1");
           t3.setName("窗口1");
    
           t1.start();
           t2.start();
           t3.start();
        }
    
    }
    

    总结:Synchronized与lock的异同?

    相同:二者都可以解决线程安全问题
    不同:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器
    lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(同时以为着lock的方式更为灵活)

    优先使用顺序:
    LOCK-》同步代码块-》同步方法

    判断线程是否有安全问题,以及如何解决:

    1.先判断是否多线程
    2.再判断是否有共享数据
    3.是否并发的对共享数据进行操作
    4.选择上述三种方法解决线程安全问题

    例题:

    	package com.example.paoduantui.Thread;
    	
    	/***
    	 * 描述:甲乙同时往银行存钱,存够3000
    	 *
    	 *
    	 * */
    	
    	//账户
    	class Account{
    	    private double balance;//余额
    	    //构造器
    	    public Account(double balance) {
    	        this.balance = balance;
    	    }
    	    //存钱方法
    	    public synchronized void deposit(double amt){
    	        if(amt>0){
    	            balance +=amt;
    	            try {
    	                Thread.sleep(1000);
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	            System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
    	        }
    	    }
    	}
    	
    	//两个顾客线程
    	class Customer extends Thread{
    	     private Account acct;
    	
    	     public Customer(Account acct){
    	         this.acct = acct;
    	     }
    	
    	
    	
    	    @Override
    	    public void run() {
    	        for (int i = 0;i<3;i++){
    	            acct.deposit(1000);
    	        }
    	    }
    	}
    	
    	//主方法,之中new同一个账户,甲乙两个存钱线程。
    	public class AccountTest {
    	
    	    public static void main(String[] args){
    	        Account acct = new Account(0);
    	        Customer c1 = new Customer(acct);
    	        Customer c2 = new Customer(acct);
    	
    	        c1.setName("甲");
    	        c2.setName("乙");
    	
    	        c1.start();
    	        c2.start();
    	    }
    	
    	}
    

    解决单例模式的懒汉式的线程安全问题:

    单例:只能通过静态方法获取一个实例,不能通过构造器来构造实例
    1.构造器的私有化:
    private Bank(){}//可以在构造器中初始化东西
    private static Bank instance = null;//初始化静态实例

    public static Bank getInstance(){
    if(instance!=null){
    instance = new Bank();
    }
    return instance;
    }

    假设有多个线程调用此单例,而调用的获取单例的函数作为操作共享单例的代码块并没有解决线程的安全问题,会导致多个线程都判断实例是否为空,此时就会导致多个实例的产生,也就是单例模式的线程安全问题。

    解决线程安全问题的思路:

    1. 将获取单例的方法改写成同部方法,即加上synchronized关键字,此时同步监视器为当前类本身。(当有多个线程并发的获取实例时,同时只能有一个线程获取实例),解决了单例模式的线程安全问题。
    2. 用同步监视器包裹住同步代码块的方式。

    懒汉式单例模式的模型,例如:生活中的限量版的抢购:
    当一群人并发的抢一个限量版的东西的时候,可能同时抢到了几个人,他们同时进入了房间(同步代码块内)
    但是只有第一个拿到限量版东西的人才能到手,其余人都不能拿到,所以效率稍高的做法是,当东西被拿走时,我们在门外立一块牌子,售罄。
    这样就减少了线程的等待。即下面效率稍高的懒汉式写法:

    package com.example.paoduantui.Thread;
    
    public class Bank {
        //私有化构造器
        private Bank(){}
        //初始化静态实例化对象
        private static  Bank instance = null;
    
        //获取单例实例,此种懒汉式单例模式存在线程不安全问题(从并发考虑)
    
        public static  Bank getInstance(){
            if(instance==null){
                instance = new Bank();
            }
            return  instance;
        }
    
        //同步方法模式的线程安全
        public static synchronized Bank getInstance1(){
            if(instance==null){
                instance = new Bank();
            }
            return  instance;
        }
        //同步代码块模式的线程安全(上锁)
        public  static Bank getInstance2(){
            synchronized (Bank.class){
                if(instance==null){
                    instance = new Bank();
                }
                return  instance;
            }
        }
        
        //效率更高的线程安全的懒汉式单例模式
        /**
         * 由于当高并发调用单例模式的时候,类似于万人夺宝,只有第一个进入房间的人才能拿到宝物,
         * 当多个人进入这个房间时,第一个人拿走了宝物,也就另外几个人需要在同步代码块外等候,
         * 剩下的人只需要看到门口售罄的牌子即已知宝物已经被夺,可以不用进入同步代码块内,提高了效率。
         * 
         * 
         * */
        public static Bank getInstance3(){
            if (instance==null){
                synchronized (Bank.class){
                    if(instance==null){
                        instance = new Bank();
                    }
                }
            }
            return  instance;
        }
    }
    

    线程的死锁问题:

    线程死锁的理解:僵持,谁都不放手,一双筷子,我一只你一只,都等对方放手(死锁,两者都进入阻塞,谁都吃不了饭,进行不了下面吃饭的操作)
    出现死锁以后,不会出现提示,只是所有线程都处于阻塞状态,无法继续

    package com.example.paoduantui.Thread;
    
    
    /**
     * 演示线程的死锁问题
     *
     * */
    public class Demo {
    
        public static void main(String[] args){
    
            final StringBuffer s1 = new StringBuffer();
            final StringBuffer s2 = new StringBuffer();
    
    
            new Thread(){
                @Override
                public void run() {
                    //先拿锁一,再拿锁二
                    synchronized (s1){
                        s1.append("a");
                        s2.append("1");
    
                        synchronized (s2) {
                            s1.append("b");
                            s2.append("2");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }.start();
    
            //使用匿名内部类实现runnable接口的方式实现线程的创建
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (s2){
                        s1.append("c");
                        s2.append("3");
    
                        synchronized (s1) {
                            s1.append("d");
                            s2.append("4");
    
                            System.out.println(s1);
                            System.out.println(s2);
                        }
                    }
                }
            }).start();
        }
    
    }
    

    运行结果:
    1.先调用上面的线程,再调用下面的线程:
    在这里插入图片描述
    2.出现死锁:
    在这里插入图片描述
    3.先调用下面的线程,再调用上面的线程。
    在这里插入图片描述

    死锁的解决办法:

    1.减少同步共享变量
    2.采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题
    3.减少锁的嵌套。

    线程的通信

    通信常用方法:

    通信方法 描述
    wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
    notify 一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程,就唤醒优先级高的线程
    notifyAll 一旦执行此方法,就会唤醒所有被wait()的线程

    使用前提:这三个方法均只能使用在同步代码块或者同步方法中。

    package com.example.paoduantui.Thread;
    
    
    /**
     * 线程通信的例子:使用两个线程打印1—100,线程1,线程2交替打印
     *
     * 当我们不采取线程之间的通信时,无法达到线程1,2交替打印(cpu的控制权,是自动分配的)
     * 若想达到线程1,2交替打印,需要:
     * 1.当线程1获取锁以后,进入代码块里将number++(数字打印并增加)操作完以后,为了保证下个锁为线程2所有,需要将线程1阻塞(线程1你等等wait())。(输出1,number为2)
     * 2.当线程2获取锁以后,此时线程1已经不能进入同步代码块中了,所以,为了让线程1继续抢占下一把锁,需要让线程1的阻塞状态取消(通知线程1不用等了notify()及notifyAll()),即应该在进入同步代码块时取消线程1的阻塞。
     *
     * */
    
    class Number implements Runnable{
    
        private int number = 1;//设置共享数据(线程之间对于共享数据的共享即为通信)
    
    
        //对共享数据进行操作的代码块,需要线程安全
        @Override
        public synchronized void run() {
    
            while(true){
                //使得线程交替等待以及通知交替解等待
                notify();//省略了this.notify()关键字
                if(number<100){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }
        }
    }
    
    public class CommunicationTest {
    
        public static void main(String[] args){
            //创建runnable对象
            Number number = new Number();
    
            //创建线程,并实现runnable接口
            Thread t1 = new Thread(number);
            Thread t2 = new Thread(number);
    
            //给线程设置名字
            t1.setName("线程1");
            t2.setName("线程2");
    
            //开启线程
            t1.start();
            t2.start();
    
        }
    
    }
    

    sleep和wait的异同:

    相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
    不同点:
    1.两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
    2.调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
    3.关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放

    经典例题:生产者/消费者问题:

    生产者(Priductor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如20个),如果生产者视图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产:如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

    这里可能出现两个问题:
    生产者比消费者快的时候,消费者会漏掉一些数据没有收到。
    消费者比生产者快时,消费者会去相同的数据。

    package com.example.paoduantui.Thread;
    
    
    /**
     * 线程通信的应用:生产者/消费者问题
     *
     * 1.是否是多线程问题?是的,有生产者线程和消费者线程(多线程的创建,四种方式)
     * 2.多线程问题是否存在共享数据? 存在共享数据----产品(同步方法,同步代码块,lock锁)
     * 3.多线程是否存在线程安全问题? 存在----都对共享数据产品进行了操作。(三种方法)
     * 4.是否存在线程间的通信,是,如果生产多了到20时,需要通知停止生产(wait)。(线程之间的通信问题,需要wait,notify等)
     *
     * */
    
    
    	class Clerk{
    	
    	    private int productCount = 0;
    	
    	
    	    //生产产品
    	    public synchronized void produceProduct() {
    	
    	        if(productCount<20) {
    	            productCount++;
    	
    	            System.out.println(Thread.currentThread().getName()+":开始生产第"+productCount+"个产品");
    	            notify();
    	        }else{
    	            //当有20个时,等待wait
    	            try {
    	                wait();
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    	
    	    //消费产品
    	    public synchronized void consumeProduct() {
    	        if (productCount>0){
    	            System.out.println(Thread.currentThread().getName()+":开始消费第"+productCount+"个产品");
    	            productCount--;
    	            notify();
    	        }else{
    	            //当0个时等待
    	            try {
    	                wait();
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	        }
    	    }
    	}
    	
    	class Producer extends Thread{//生产者线程
    	
    	    private Clerk clerk;
    	
    	    public Producer(Clerk clerk) {
    	        this.clerk = clerk;
    	    }
    	
    	    @Override
    	    public void run() {
    	
    	        try {
    	            sleep(10);
    	        } catch (InterruptedException e) {
    	            e.printStackTrace();
    	        }
    	        System.out.println(Thread.currentThread().getName()+";开始生产产品......");
    	
    	        while(true){
    	            clerk.produceProduct();
    	        }
    	    }
    	}
    	
    	class Consumer implements Runnable{//消费者线程
    	
    	    private Clerk clerk;
    	
    	    public Consumer(Clerk clerk) {
    	        this.clerk = clerk;
    	    }
    	
    	    @Override
    	    public void run() {
    	
    	        System.out.println(Thread.currentThread().getName()+":开始消费产品");
    	
    	        while(true){
    	            try {
    	                Thread.sleep(1);
    	            } catch (InterruptedException e) {
    	                e.printStackTrace();
    	            }
    	
    	            clerk.consumeProduct();
    	        }
    	
    	    }
    	}
    	
    	public class ProductTest {
    	
    	    public static void main(String[] args){
    	        Clerk clerk = new Clerk();
    	
    	        Producer p1 = new Producer(clerk);
    	        p1.setName("生产者1");
    	
    	        Consumer c1 = new Consumer(clerk);
    	        Thread t1 = new Thread(c1);
    	        t1.setName("消费者1");
    	
    	        p1.start();
    	        t1.start();
    	
    	    }
    	
    	}
    
    展开全文
  • 运行环境为VS2015,如果直接在多线程中操作GUI会报错,在.net中,可以通过Dispatcher.Invoke来委托进行操作具体用法示例:节选自该程序中接收下位机发送数据并显示的代码1.先在其他函数中创建并启动线程ThreadStart ...
  • 线程、进程、多线程、多进程 和 多任务 小结

    千次阅读 多人点赞 2019-04-20 11:59:56
    4 多线程 5 线程与进程的关系 6 线程和进程的区别 7 进程的优缺点 7.1 进程的优点 7.2 进程的缺点 8 线程的优缺点 8.1 线程的优点 8.2 线程的缺点 9 多线程的优缺点 9.1 多线程的优点 9.2 多线程的缺点 ...
  • 任何收发两端速度不一致的通讯,都需要在它们之间使用一个足够大的FIFO缓冲区。...◆多线程锁 ◆多线程日志 ◆日志文件占用的磁盘空间的可控性。 ◆日志中的时间包括毫秒 ◆传输的数据对应的每个字节到底的英文...
  • c++多线程编程与MFC多线程编程

    千次阅读 2012-03-16 15:52:18
    源代码1:http://download.csdn.net/detail/nuptboyzhb/4160217 源码2:...   (一)有关多线程的WIN32 API函数 1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes
  • Android 多线程

    千次阅读 2019-05-29 15:32:43
    Android 多线程:手把手教你使用AsyncTask 前言 多线程的应用在Android开发中是非常常见的,常用方法主要有: 继承Thread类 实现Runnable接口 Handler AsyncTask HandlerThread 下面讲的是AsyncTask: 目录 示意图 ...
  • 多线程

    千次阅读 2016-12-23 14:03:01
    1多线程的概念 多线程编程的含义是你可将程序任务分成几个并行的子任务。特别是在网络编程中,你会发现很多功能是可以并发执行的。比如网络传输速度较慢,用户输入速度较慢,你可以用两个独立的线程去完成这?copy;...
  • C#多线程编程

    千次阅读 2014-06-14 11:47:27
    本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发。 其中委托的BeginInvoke方法以及回调函数最为常用。 而 I/O线程可能容易遭到大家的忽略,其实在...
  • 多线程技术

    千次阅读 2016-11-07 15:29:41
    多任务、多线程和多处理这些术语经常被交替地使用,但是它们在本质上是不同的概念。多任务是指操作系统具有在任务间快速切换使得这些任务看起来是在同步执行的能力。在一个抢占式多任务系统中,应用程序可以随时被...
  • 二Qt多线程方法一 继承QThread 2.1使用多线程的原因分析 2.2 写一个继承于QThread的线程 三 总结 一 前言   本篇文章部分内容参考了该博文:传送门。   Qt中有两种多线程的方法,其中一种是继承...
  • Linux多线程编程

    千次阅读 2011-08-26 10:24:55
    线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期, solaris是这方面的佼佼者。传统的Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就...
  • Java多线程游戏仿真实例分享

    万次阅读 多人点赞 2021-02-02 18:20:57
    多线程的创建、多线程的应用、多线程的特点以及多线程的注意事项) 2、如何让小球在画面中真实地动起来?(赋予小球匀速直线、自由落体、上抛等向量运动) 3、多线程游戏仿真实例分享(飞机大战、接豆人、双线挑战...
  • Android多线程方式

    万次阅读 多人点赞 2018-03-26 22:58:22
    在Android开发中经常会使用到多线程,这里主要是总结Android开发中常见的多线程实现方式,以及这些多线程实现方式的一些特点 多线程实现方式主要有: 实现Thread的run()方法或者实现Runable接口 HandlerThread ...
  • QT多线程编程详解

    万次阅读 多人点赞 2019-04-24 22:08:20
    一、线程基础 1、GUI线程与工作线程 每个程序启动后拥有的第一个线程称为主线程,即GUI线程。QT中所有的组件类和几个相关的类只能工作在GUI线程,不能工作在次...二、QT多线程简介 QT通过三种形式提供了对线程...
  • python中的多任务-多线程和多进程

    千次阅读 2019-03-10 11:12:29
    多线程和多进程都是实现多任务的一种方式,但是对于很多初学者来说想分清楚他们往往是一件非常头疼的事,首先我们需要了解多任务的概念。 所谓的多任务就是在同一时刻同时做很多事情,比如我们一边使用浏览器...
  • Python 多线程是多鸡肋

    千次阅读 2016-11-29 11:52:16
    摘要:Python 对并行化支持的名声就不是很好,如果你用过 Python 自带的线程库 thread 和 threading,...Python 多线程 基础 Python 多线程 阻塞 Python 多线程 获取返回值 Python 多线程 数据对比测试 正文: 一.
  • Java多线程详解

    千次阅读 2016-04-12 20:55:01
    Java多线程详解         多线程简介 概述 多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体...
  • QT 多线程程序设计

    千次阅读 2014-03-15 14:49:18
    这使得开发轻巧的多线程Qt程序更为容易,并能充分利用多处理器机器的优势。多线程编程也是一个有用的模式,它用于解决执行较长时间的操作而不至于用户界面失去响应。在Qt的早期版本中,在构建库时有不选择线程支持的...
  • c#多线程

    千次阅读 2012-07-04 12:29:49
    本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发。 其中委托的BeginInvoke方法以及回调函数最为常用。 而 I/O线程可能容易遭到大家的忽略,其实在...
  • QSerialport多线程方法

    千次阅读 2019-05-20 07:19:42
    QSerialport多线程方法 使用Qt也已经有一段时间了,虽然使用过继承QThread重写run函数,以及继承QObject然后使用MoveToThread两种方法实现多线程,但是在QSerialPort的使用过程中,两种方法都存在一定的问题。 ...
  • 多线程特别重要,虽然内容偏多,但是需要熟练掌握。面试也会在此章节有考验的!请大家耐心学习! 目录 一、什么是线程 二、线程的组成(创建线程方式) 三、线程的状态(方法实例详解) 四、线程安全(实例详解)...
  • JAVA多线程并发

    千次阅读 多人点赞 2019-09-18 12:14:29
    JAVA多线程并发1 JAVA并发知识库2 JAVA 线程实现/创建方式2.1 继承 Thread 类2.2 实现 Runnable 接口2.3 Callable 、Future 、ExecutorService 有返回值线程2.4 基于线程池的方式2.4.1 4种线程池2.4.1.1 ...
  • Qt多线程

    千次阅读 2012-08-12 23:16:05
    QT通过三种形式提供了对线程的支持。它们分别是,一、平台无关的线程类,二、线程安全的事件投递,三、跨线程的信号-槽连接。...多线程编程也是一个有用的模式,它用于解决执行较长时间的操作而不至于用
  • 5.5.1 多线程设计

    2008-05-09 17:47:00
    客户端设计为多线程工作方式,主线程显示用户的输入和数据的计算结果。子线程接收和发送数据。q 主线程用于所有的界面显示接收用户的输入。q 建立一个数据发送数据线程。当数据打包后,通知该线程向服务器发送...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 177,788
精华内容 71,115
关键字:

多线程接收显示