精华内容
下载资源
问答
  • socket 长连接 简单例子,适合初学的朋友,里面有多线程 实现的,包括心跳包,数据分为两部分传送,首先双方约定用一个4字节的数组告诉对方要传送数据的长度,然后在写入数据,这样长连接的时候,双方可以知道对方...
  • C有两个线程:一个收数据;一个将数据保存到本地,保存成功以后向S回复一个确认。 因为是长连接,所以要加一个心跳检测,C每隔100秒向S发一个心跳数据包,S收到包以后会给C一个回复。如果C没有在10秒内收到回复,...
  • 此系列例程演示了多线程TCP挂机+心跳的多种方案 *被动心跳(并发型) *主动心跳(并发型) *实时数据展示 *实时状态展示 *回应包同步方案 *连接附加数据管理方案 数组/哈希表 *HTTP/HTTPS,SOSKS5代理按连接分配方案 *...
  • 此系列例程演示了多线程TCP挂机+心跳的多种方案 *被动心跳(并发型) *主动心跳(并发型) *实时数据展示 *实时状态展示 *回应包同步方案 *连接附加数据管理方案 数组/哈希表 *HTTP/HTTPS,SOSKS5代理按连接分配方案 各...
  • 此系列例程演示了多线程TCP挂机 心跳的多种方案*被动心跳(并发型)*主动心跳(并发型)*实时数据展示*实时状态展示*回应包同步方案*连接附加数据管理方案 数组/哈希表*HTTP/HTTPS,SOSKS5代理按连接分配方案各版本差异:...
  • 前阶段的一个项目,需要实现socket的长连接,即需要实现心跳连接,由于之前只做过简单的socket通讯,所以没有太的相关知识,只能在度娘上边儿潜水,从0开始学习心跳机制,其实,只要稍微了解网络通讯的业界大佬...

    前阶段的一个项目,需要实现socket的长连接,即需要实现心跳连接,由于之前只做过简单的socket通讯,所以没有太多的相关知识,只能在度娘上边儿潜水,从0开始学习心跳机制,其实,只要稍微了解网络通讯的业界大佬对此应该都是不屑的。“心跳”说白了就是为了保证长连接,在正常的socket通讯中,只要服务端socket和客户端socket连接成功后,就可以进行数据的传递了,但是有些时候,服务器端不知道客户端什么时候发来数据,则需要进行阻塞式的接收数据,这就形成了长连接,但是如何保证二者一直处于连接状态呢?这就需要一个类似于监控设备的东西来定时的报告一次,是否连接,这就形成了“心跳”,二者之间的通过发送反馈一个既定的字节或者其他数据,来监听连接是否正常,从而告诉客户端此时的连接状态。

    以下是我的项目中的一段片段代码:

    import android.app.Activity;
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.util.Log;
    import com.example.rkgg.UI.DBManager;
    import com.example.rkgg.UI.TypeConversion;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.lang.ref.WeakReference;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
     * Created by RKGG on 2017/9/21. */
    
    public class BackService extends Service {
        private static final String TAG = "BackService";
        /**
         * 心跳检测时间 */
        private static final long HEART_BEAT_RATE = 25 * 1000;
        /**
         * 常规数据心跳时间 */
        private static final long REGULAR_BEAT_RATE = 30 * 1000;
        /**
         * 主机IP */
        private static String HOST = "";
        /**
         * 端口号 */
        public static final int PORT = 8865;
        /**
         * 登录广播 */
        public static final String LOGIN_ACTION = "com.example.login_ACTION";
        /**
         * 消息广播 */
        public static final String MESSAGE_ACTION = "com.example.message_ACTION";
        /**
         * 心跳广播 */
        public static final String HEART_BEAT_ACTION = "com.example.heart_beat_ACTION";
        private boolean HEARTBEAT_NORMAL = false;
        /**
         * 登出广播 */
        public static final String LOGOUT_ACTION = "com.example.logout_ACTION";
        /**
         * 释放资源广播 */
        public static final String RELEASESOCKET_ACTION = "com.example.releaseSocket_ACTION";
    
        private long sendTime = 0L;
        private String startHours;
        private String endHours;
        private int workCounts;
        private int workTime;
        public DBManager dbManager;
        /**
         * 弱引用 在引用对象的同时允许对垃圾对象进行回收 */
        public WeakReference<Socket> mSocket;
        private Object lock = new Object();
        private ReadThread mReadThread;
        private TypeConversion tc;
        private TcpCommond tcd;
        public static boolean flag = false;
        public static int socket_flag = 0;
        public static int logout_flag = 0;
        //返回的云空间结构
        public static byte[] cloud_results;
        //下载文件的内容
        public static byte[] file_down;
        //分块数据长度
        public static byte[] block_down;
        private IBackService.Stub iBackService = new IBackService.Stub() {
            @Override
            public boolean sendMessage(String message) throws RemoteException {
                String s = "";
                byte[] bs = tc.hexStringToByteArray(message);
                return sendMsg(bs);
            }
    
            @Override
            public boolean sendCommond(byte[] bytes) throws RemoteException {
                return sendMsg(bytes);
            }
        };
    
    
        @Override
        public IBinder onBind(Intent arg0) {
            return iBackService;
        }
    
    
        @Override
        public void onCreate() {
            super.onCreate();
            dbManager = new DBManager(this);
            tc = new TypeConversion();
            tcd = new TcpCommond(this);
            new InitSocketThread().start();
        }
    
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            //在销毁service的时候,结束线程,这里不需要再释放socket
            if (heartHandler.getLooper().getThread().getState().toString().equals("RUNNABLE")) {
                heartHandler.removeCallbacks(heartBeatRunnable);
            }
            if (regularHandler.getLooper().getThread().getState().toString().equals("RUNNABLE")) {
                regularHandler.removeCallbacks(regularRunnable);
            }
            sendMsg(tcd.deviceLogout());
            flag = false;
            socket_flag = 0;
        }
    
    
        // 发送心跳包
        public Handler heartHandler = new Handler();
        public Runnable heartBeatRunnable = new Runnable() {
            @Override
            public void run() {
                if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            if (flag == true) {//判断socket是否连连接
                                boolean isSuccess = sendMsg(tcd.heartBeatbag());//如果发送失败,就重新初始化一个socket
                                if (!isSuccess) {
                                    heartHandler.removeCallbacks(heartBeatRunnable);
                                    mReadThread.release();
                                    releaseLastSocket(mSocket);
                                    new InitSocketThread().start();
                                }
                            }
                        }
                    }).start();
                }
                heartHandler.postDelayed(this, HEART_BEAT_RATE);
            }
        };
    
        //常规数据
        private Handler regularHandler = new Handler();
        private Runnable regularRunnable = new Runnable() {
            @Override
            public void run() {
                if (System.currentTimeMillis() - sendTime >= REGULAR_BEAT_RATE) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            if (flag == true) {
                                boolean isSuccess = sendMsg(tcd.regularData());
                                if (!isSuccess) {
                                    regularHandler.removeCallbacks(regularRunnable);
                                    mReadThread.release();
                                    releaseLastSocket(mSocket);
                                    new InitSocketThread().start();
                                }
                            }
                        }
                    }).start();
                }
                regularHandler.postDelayed(this, REGULAR_BEAT_RATE);
            }
        };
    
        //发送定位数据
        private Handler gpsHandler = new Handler();
        private Runnable gpsRunnable = new Runnable() {
            @Override
            public void run() {
                if (HEARTBEAT_NORMAL) {
                    boolean isSuccess = sendMsg(tcd.positioningData());
                    if (!isSuccess) {
                        gpsHandler.removeCallbacks(gpsRunnable);
                        mReadThread.release();
                        sendMsg(tcd.positioningData());
                        releaseLastSocket(mSocket);
                        new InitSocketThread().start();
                    }
                }
                gpsHandler.post(this);
            }
        };
    
        public boolean sendMsg(byte[] bytes) {
            if (null == mSocket || null == mSocket.get()) {
                return false;
            }
            Socket soc = mSocket.get();
            try {
                if (!soc.isClosed() && !soc.isOutputShutdown()) {
                    OutputStream os = soc.getOutputStream();
                    try {
                        os.write(bytes);
                        os.flush();
                    }catch (Exception e){
                        Log.e("MainActivity","Socket is disconnected!");
                    }
                    // 每次发送成功数据,就改一下最后成功发送的时间,节省心跳间隔时间
                    sendTime = System.currentTimeMillis();
                } else {
                    return false;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        // 初始化socket
        private void initSocket() {
            Socket socket = null;
            try {
    			// 域名解析
                HOST = InetAddress.getByName("xxxx.xx.xx").getHostAddress();
                socket = new Socket(HOST, PORT);
                mSocket = new WeakReference<Socket>(socket);
            }catch (Exception e){}
            sendMsg(tcd.deviceLogin());//登陆设备
            mReadThread = new ReadThread(socket);
            mReadThread.start();
        }
    
        // 释放socket
        public void releaseLastSocket(WeakReference<Socket> mSocket) {
                if (null != mSocket) {
                    sendMsg(tcd.deviceLogout());
                    Socket sk = mSocket.get();
                    try {
                        if (!sk.isClosed()) {
                            sk.close();
                        }
                        sk = null;
                        mSocket = null;
                    }catch (Exception e){
                        System.out.println("NULL!!!");
                    }
                }
        }
    
        class InitSocketThread extends Thread {
            @Override
            public void run() {
                super.run();
                try {
                    initSocket();
                }catch (Exception e){
                    System.out.println("端口占用!!!");
                }
            }
        }
    
        class ReadThread extends Thread {
            private WeakReference<Socket> mWeakSocket;
            private boolean isStart = true;
    
            public ReadThread(Socket socket) {
                mWeakSocket = new WeakReference<Socket>(socket);
            }
    
            public void release() {
                isStart = false;
                sendMsg(tcd.deviceLogout());
                releaseLastSocket(mWeakSocket);
            }
    
            //同步方法读取返回得数据
            @Override
            public void run() {
                super.run();
                Socket socket = mWeakSocket.get();
                if (null != socket) {
                    try {
                        InputStream is = socket.getInputStream();
                        byte[] buffer = new byte[65535];//测试过程发现接收的文件会达到5000多字节 
                        int length;
                        try {
                            while (!socket.isClosed() && !socket.isInputShutdown()
                                    && isStart && ((length = is.read(buffer)) != -1)) {
                                synchronized (this) {
                                    if (length > 0) {
                                        byte[] temp = new byte[length];
                                        System.arraycopy(buffer, 0, temp, 0, length);
                                        //获取返回值的第3位,表明命令类型
                                        String msg_three = tc.bytesToHexString(temp).substring(4, 6);
                                        //获取返回值的第4位,表明数据返回的情况
                                        String msg_four = tc.bytesToHexString(temp).substring(6, 8);
                                        // 收到服务器过来的消息,就通过Broadcast发送出去
                                        if (msg_three.equals("01")) {//登陆
                                            switch (msg_four) {
                                                case "01":
                                                    Log.i(TAG, "登录成功");
                                                    //先发送一个常规包,确定在线情况
                                                    flag = true;
                                                    socket_flag = 1;
                                                    sendMsg(tcd.regularData());
                                                    //初始化成功返回标志后,开始发送心跳包
                                                    heartHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);
                                                    //发送广播,防止不同的activity重新new一个Serivce
                                                    Intent intent = new Intent(LOGIN_ACTION);
                                                    intent.putExtra("condition", "Socket_Connected");
                                                    sendBroadcast(intent);
                                                    break;
                                                case "02":
                                                    Log.i(TAG, "登录超时");
                                                    mReadThread.release();
                                                    releaseLastSocket(mSocket);
    //                                                new InitSocketThread().start();
                                                    break;
                                                case "03":
                                                    Log.i(TAG, "账号密码错误");
                                                    mReadThread.release();
                                                    releaseLastSocket(mSocket);
    //                                                new InitSocketThread().start();
                                                    break;
                                                case "04":
                                                    Log.i(TAG, "其他位置登录");
                                                    //需要在销毁的时候登出设备,否则会出现这个错误
                                                    mReadThread.release();
                                                    releaseLastSocket(mSocket);
    //                                                new InitSocketThread().start();
                                                    break;
                                                default:
                                                    break;
                                            }
                                        } else if (msg_three.equals("02")) {//实时上报
                                            switch (msg_four) {
                                                case "01":
                                                    Log.i(TAG, "实时信息上报成功");
                                                    break;
                                                case "02":
                                                    Log.i(TAG, "实时信息上报超时");
                                                    break;
                                                default:
                                                    break;
                                            }
                                        } else if (msg_three.equals("03")) {// 心跳
                                            switch (msg_four) {
                                                case "01":
                                                    Log.i(TAG, "心跳正常");
                                                    //心跳正常后,开始发送常规数据,经测试发现,常规数据也需要定时发送,
                                                    //否则发送一次后,大概1min左右会出现掉线状态
                                                    regularHandler.postDelayed(regularRunnable, REGULAR_BEAT_RATE);
                                                    break;
                                                case "":
                                                    break;
                                            }
                                        } else if (msg_three.equals("05")) {//登出
                                            switch (msg_four) {
                                                case "01":
                                                    Log.i(TAG, "登出成功");
                                                    logout_flag = 1;
                                                    mReadThread.release();
                                                    releaseLastSocket(mSocket);
                                                    break;
                                                case "02":
                                                    Log.i(TAG, "登出超时");
                                                    break;
                                                default:
                                                    break;
                                            }
                                        }  else {
                                            // 其他消息回复
                                            Intent intent = new Intent(MESSAGE_ACTION);
                                            intent.putExtra("message", tc.bytesToHexString(temp));
                                            sendBroadcast(intent);
                                        }
                                    }
                                }
                            }
                        }catch (Exception e){
                            flag = false;
                            socket_flag = 0;
                            //释放socket
                            mReadThread.release();
                            releaseLastSocket(mSocket);
    //                        Intent intent = new Intent(MESSAGE_ACTION);
    //                        intent.putExtra("message", "服务器已断开!");
    //                        sendBroadcast(intent);
                            System.out.println("网络异常!");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    } 

    上面的代码主要有几点要说一下:

    1.利用handler+runnable实现定时器,将心跳包按照规定的时间定时发送, 而在runnable中创建一个新的线程,这样避免了心跳包发送在UI线程中占用资源。所以在销毁service的时候,需要捕获线程并结束线程(这是我的项目需求,所以一般如果需要保持后台依然连接的话,请忽略),

    2.利用同步的方法读取服务器返回的数据,因为有”心跳机制“的存在,在数据接收的时候,就会出现可能数据同一时刻返回的情况,所以需要用一个锁来进行锁定,读取一条返回的指令后,再读取下一条指令。

    参考自以下几篇文章:

    http://blog.csdn.net/androidforwell/article/details/55102185

    http://blog.csdn.net/ttkatrina/article/details/72956338

    http://blog.csdn.net/jj583500596/article/details/76586696

    展开全文
  • Qt多线程方法1 继承QThread 1写一个继承于QThread的线程 2 QThread的几个函数quitexitterminate函数 3 正确的终止一个线程 4 如何正确启动一个线程 41正确的启动一个全局线程和UI一直存在的线程 42 如何启动一...


    使用QObject实现多线的方法见:http://blog.csdn.net/czyt1988/article/details/71194457

    1.摘要

    Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。
    Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。两种方法区别不大,用起来都比较方便,但继承QObject的方法更加灵活。这里要记录的是如何正确的创建一个线程,特别是如何正确的退出一个线程。

    本文先介绍QThread的普通用法,这个用法可能网上很多文章都介绍过,如果已经了解大可跳过此节,本文重点介绍线程退出的几种方法,根据需求正确的创建和退出线程等问题。

    2.Qt多线程方法1 继承QThread

    在使用继承QThreadrun方法之前需要了解一条规则:

    QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里

    QThread只有run函数是在新线程里的
    QThread只有run函数是在新线程里的
    QThread只有run函数是在新线程里的

    重要的事情说3遍!!!

    ,如果QThread是在ui所在的线程里生成,那么QThread的其他非run函数都是和ui线程一样的,所以,QThread的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run函数里。
    在UI线程下调用QThread的非run函数(其实也不应该直接调用run函数,而应该使用start函数),和执行普通函数无区别,这时,如果这个函数要对QThread的某个变量进行变更,而这个变量在run函数里也会被用到,这时就需要注意加锁的问题,因为可能这个变量前几毫秒刚刚在run中调用,再调用时已经被另外的线程修改了。

    2.1写一个继承于QThread的线程

    本文的重点不是教会你继承run写一个多线程,任何有编程基础的5分钟就能学会使用QThread的方法,本文真正要讲的是后面那几节,如如何安全的退出一个线程,如何开启一个临时线程,运行结束马上关闭等问题。如果你对QThread有初步了解,那么可以略过这节,但你最好看看这节后面提出的几个问题。

    任何继承于QThread的线程都是通过继承QThreadrun函数来实现多线程的,因此,必须重写QThreadrun函数,把复杂逻辑写在QThreadrun函数中。

    看看一个普通继承QThread的例子:
    头文件:

    #ifndef THREADFROMQTHREAD_H
    #define THREADFROMQTHREAD_H
    #include <QThread>
    
    class ThreadFromQThread : public QThread
    {
        Q_OBJECT
    signals:
        void message(const QString& info);
        void progress(int present);
    public:
        ThreadFromQThread(QObject* par);
        ~ThreadFromQThread();
        void setSomething();
        void getSomething();
        void setRunCount(int count);
        void run();
        void doSomething();
    private:
        int m_runCount;
    };
    
    #endif // THREADFROMQTHREAD_H
    

    cpp文件:

    #include "ThreadFromQThread.h"
    #include <QDebug>
    ThreadFromQThread::ThreadFromQThread(QObject* par) : QThread(par)
    ,m_runCount(20)
    {
    
    }
    
    ThreadFromQThread::~ThreadFromQThread()
    {
        qDebug() << "ThreadFromQThread::~ThreadFromQThread()";
    }
    
    void ThreadFromQThread::setSomething()
    {
        msleep(500);
        QString str = QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId());
        emit message(str);
    }
    
    void ThreadFromQThread::getSomething()
    {
        msleep(500);
        emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    }
    
    void ThreadFromQThread::setRunCount(int count)
    {
        m_runCount = count;
        emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    }
    
    void ThreadFromQThread::run()
    {
        int count = 0;
        QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
        emit message(str);
        while(1)
        {
            sleep(1);
            ++count;
            emit progress(((float)count / m_runCount) * 100);
            emit message(QString("ThreadFromQThread::run times:%1").arg(count));
            doSomething();
            if(m_runCount == count)
            {
                break;
            }
        }
    }
    
    void ThreadFromQThread::doSomething()
    {
        msleep(500);
        emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));    
    }
    
    

    这个简单的例子有一个Qt类常见的内容,包含了普通方法,信号槽,和一个run函数。这里函数setSomething();进行了500ms的延迟,getSomething同理。这是为了验证在QThread::run()之外调用QThread成员函数不会运行在新线程里。

    上面代码用到了QThread::currentThreadId()这是一个静态函数,用于返回当前线程句柄,这个值除了区分以外没有别的用处。

    为了验证这个线程,编写一个简单的界面,这个界面主要用于验证如下几个问题:、

    • 在UI线程调用setSomething();函数和getSomething();函数会不会卡顿?
    • 在UI线程调用QThread::quit()QThread::exit()函数会不会停止线程?
    • 在UI线程调用QThread::terminate函数会不会停止线程?
    • 如何正确的退出线程?

    2.2 QThread的几个函数quit、exit、terminate函数

    为了验证上面这些,编写一个简单的界面如下图所示:

    这里写图片描述

    #include "Widget.h"
    #include "ui_Widget.h"
    #include "ThreadFromQThread.h"
    #include <QDebug>
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
        //控件初始化
        ui->progressBar->setRange(0,100);
        ui->progressBar->setValue(0);
        ui->progressBar_heart->setRange(0,100);
        ui->progressBar_heart->setValue(0);
        //按钮的信号槽关联
        connect(ui->pushButton_qthread1,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadClicked);
        connect(ui->pushButton_qthread1_setSomething,&QPushButton::clicked
                ,this,&Widget::onButtonQthread1SetSomethingClicked);
        connect(ui->pushButton_qthread1_getSomething,&QPushButton::clicked
                ,this,&Widget::onButtonQthread1GetSomethingClicked);
        connect(ui->pushButton_qthreadQuit,&QPushButton::clicked
                ,this,&Widget::onButtonQthreadQuitClicked);
        connect(ui->pushButton_qthreadTerminate,&QPushButton::clicked
                ,this,&Widget::onButtonQthreadTerminateClicked);
        connect(ui->pushButton_qthreadExit,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadExitClicked);
        connect(ui->pushButton_doSomthing,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadDoSomthingClicked);
        connect(ui->pushButton_qthreadRunLocal,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadRunLoaclClicked);
        //
        connect(ui->pushButton_qobjectStart,&QPushButton::clicked
                ,this,&Widget::onButtonObjectMove2ThreadClicked);
        connect(ui->pushButton_objQuit,&QPushButton::clicked
                ,this,&Widget::onButtonObjectQuitClicked);
        //
        connect(&m_heart,&QTimer::timeout,this,&Widget::heartTimeOut);
        m_heart.setInterval(100);
        //全局线程的创建
        m_thread = new ThreadFromQThread(this);
        connect(m_thread,&ThreadFromQThread::message
                ,this,&Widget::receiveMessage);
        connect(m_thread,&ThreadFromQThread::progress
                ,this,&Widget::progress);
        connect(m_thread,&QThread::finished
                ,this,&Widget::onQThreadFinished);
    
        m_heart.start();
    }
    
    
    
    Widget::~Widget()
    {
        qDebug() << "start destroy widget";
        m_thread->stopImmediately();//由于此线程的父对象是Widget,因此退出时需要进行判断
        m_thread->wait();
        delete ui;
        qDebug() << "end destroy widget";
    }
    
    void Widget::onButtonQThreadClicked()
    {
        ui->progressBar->setValue(0);
        if(m_thread->isRunning())
        {
            return;
        }
        m_thread->start();
    }
    
    void Widget::progress(int val)
    {
        ui->progressBar->setValue(val);
    }
    
    void Widget::receiveMessage(const QString &str)
    {
        ui->textBrowser->append(str);
    }
    
    void Widget::heartTimeOut()
    {
        static int s_heartCount = 0;
        ++s_heartCount;
        if(s_heartCount > 100)
        {
            s_heartCount = 0;
        }
        ui->progressBar_heart->setValue(s_heartCount);
    }
    
    void Widget::onButtonQthread1SetSomethingClicked()
    {
        m_thread->setSomething();
    }
    
    void Widget::onButtonQthread1GetSomethingClicked()
    {
        m_thread->getSomething();
    }
    
    void Widget::onButtonQthreadQuitClicked()
    {
        ui->textBrowser->append("m_thread->quit() but not work");
        m_thread->quit();
    }
    
    void Widget::onButtonQthreadTerminateClicked()
    {
        m_thread->terminate();
    }
    
    void Widget::onButtonQThreadDoSomthingClicked()
    {
        m_thread->doSomething();
    }
    
    void Widget::onButtonQThreadExitClicked()
    {
        m_thread->exit();
    }
    
    void Widget::onQThreadFinished()
    {
        ui->textBrowser->append("ThreadFromQThread finish");
    }
    

    界面为上面提到的几个问题提供了按钮, 界面有一个心跳进度条,它是主程序的定时器控制,每100ms触发用于证明主程序的ui线程没有卡死。第二个进度条由线程控制。

    点击"QThread run"按钮,触发onButtonQThreadClicked槽,子线程会运行,子线程运行起来后,会打印

    …/QtThreadTest/ThreadFromQThread.cpp->run,thread id:2900388672

    可以确定线程运行的id是2900388672
    子线程是个循环,每次循环都会有打印信息:

    ThreadFromQThread::run times:1
    doSomething->…/QtThreadTest/ThreadFromQThread.cpp,thread id:2900388672
    ThreadFromQThread::run times:2
    doSomething->…/QtThreadTest/ThreadFromQThread.cpp,thread id:2900388672

    doSomething是在run函数里调用,其线程id是2900388672,可见这时doSomething函数是运行在子线程里的。

    这时,我在界面点击getSomething,setSomething,doSomething会打印:

    getSomething->…/QtThreadTest/ThreadFromQThread.cpp,thread id:3021526784
    setSomething->…/QtThreadTest/ThreadFromQThread.cpp,thread id:3021526784
    doSomething->…/QtThreadTest/ThreadFromQThread.cpp,thread id:3021526784

    说明在非run函数里调用QThread的成员函数,并不是在线程里运行(3021526784是widget所在线程)

    这时我点击quit,thread并没进行任何处理,QThread在不调用exec()情况下是exit函数和quit函数是没有作用的。

    m_thread->quit() but not work

    点击terminate按钮,线程马上终止,打印:

    ThreadFromQThread finish

    动态图如下图所示:

    因此可以看出quitexit函数都不会中途终端线程,要马上终止一个线程可以使用terminate函数,但这个函数存在非常不安定因素,不推荐使用。那么如何安全的终止一个线程呢?

    2.3 正确的终止一个线程

    最简单的方法是添加一个bool变量,通过主线程修改这个bool变量来进行终止,但这样有可能引起访问冲突,需要加锁
    我们需要在原来的头文件加上如下语句:

    #include <QMutex>
    
    class ThreadFromQThread : public QThread
    {
    ...........
    public slots:
        void stopImmediately();
    private:
        QMutex m_lock;
        bool m_isCanRun;
    .........
    };
    

    run函数需要进行修改:

    void ThreadFromQThread::stopImmediately()
    {
        QMutexLocker locker(&m_lock);
        m_isCanRun = false;
    }
    
    void ThreadFromQThread::run()
    {
        int count = 0;
        m_isCanRun = true;//标记可以运行
        QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((unsigned int)QThread::currentThreadId());
        emit message(str);
        while(1)
        {
            sleep(1);
            ++count;
            emit progress(((float)count / m_runCount) * 100);
            emit message(QString("ThreadFromQThread::run times:%1").arg(count));
            doSomething();
            if(m_runCount == count)
            {
                break;
            }
    
            {
                QMutexLocker locker(&m_lock);
                if(!m_isCanRun)//在每次循环判断是否可以运行,如果不行就退出循环
                {
                    return;
                }
            }
        }
    }
    

    QMutexLocker可以安全的使用QMutex,以免忘记解锁(有点类似std::unique_ptr),这样每次循环都会看看是否要马上终止。
    在线程需要马上退出时,可以在外部调用stopImmediately()函数终止线程,之前的例子可以知道,由于在主线程调用QThread非run()函数的函数都是在主线程运行,因此,在主线程调用类似m_thread->stopImmediately()会几乎马上把线程的成员变量m_isCanRun设置为false(面对多线程问题要用面向过程的思维思考),因此在子线程的run函数的循环中遇到m_isCanRun的判断后就会退出run函数,继承QThread的函数在运行完run函数后就视为线程完成,会发射finish信号。

    2.4 如何正确启动一个线程

    线程的启动有几种方法,这几种方法设计到它的父对象归属问题,和如何删除他的问题。首先要搞清楚这个线程是否和UI的生命周期一致,直到UI结束线程才结束,还是这个线程只是临时生成,等计算完成就销毁。

    第一种情况的线程在创建时会把生成线程的窗体作为它的父对象,这样窗体结束时会自动析构线程的对象。但这时候要注意一个问题,就是窗体结束时线程还未结束如何处理,如果没有处理这种问题,你会发现关闭窗口时会导致程序崩溃。往往这种线程是一个监控线程,如监控某个端口的线程。为了好区分,暂时叫这种叫全局线程,它在UI的生命周期中都存在。

    第二种情况是一种临时线程,这种线程一般是突然要处理一个大计算,为了不让UI假死需要触发的线程,这时需要注意一个问题,就是在线程还没计算完成,用户突然终止或变更时如何处理,这种线程往往更多见且更容易出错,如打开一个大文件,显示一个大图片,用户可能看一个大图片还没等图片处理完成又切换到下一个图片,这时绘图线程要如何处理才能顺利解决?为了好区分,暂时叫这种叫局部线程,它在UI的生命周期中仅仅是某时刻才会触发,然后销毁。

    这就涉及到如何终止正在执行的线程这个问题!

    2.4.1正确的启动一个全局线程(和UI一直存在的线程)

    我发现大部分网上的教程都是教你创建一个全局的线程,但往往这种线程用的不多,也比较好管理,需要注意的是程序退出时对线程的处理问题。
    在ui的头文件中声明一个线程的指针

    widget.h:

    ThreadFromQThread* m_thread;
    

    wodget.cpp:

    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit Widget(QWidget *parent = 0);
        ~Widget();
    private slots:
        void onButtonQThreadClicked();
        void onButtonQthread1SetSomethingClicked();
        void onButtonQthread1GetSomethingClicked();
        void onButtonQthreadQuitClicked();
        void onButtonQthreadTerminateClicked();
        void onButtonQThreadDoSomthingClicked();
        void onQThreadFinished();
    ......
        void progress(int val);
        void receiveMessage(const QString& str);
        void heartTimeOut();
    private:
        Ui::Widget *ui;
        ThreadFromQThread* m_thread;
        QTimer m_heart;
    ......
    };
    

    先看窗体生成的构造函数

    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
      ,m_objThread(NULL)
    {
       
       ui->setupUi(this);
        //控件初始化
        ui->progressBar->setRange(0,100);
        ui->progressBar->setValue(0);
        ui->progressBar_heart->setRange(0,100);
        ui->progressBar_heart->setValue(0);
        //按钮的信号槽关联
        connect(ui->pushButton_qthread1,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadClicked);
        connect(ui->pushButton_qthread1_setSomething,&QPushButton::clicked
                ,this,&Widget::onButtonQthread1SetSomethingClicked);
        connect(ui->pushButton_qthread1_getSomething,&QPushButton::clicked
                ,this,&Widget::onButtonQthread1GetSomethingClicked);
        connect(ui->pushButton_qthreadQuit,&QPushButton::clicked
                ,this,&Widget::onButtonQthreadQuitClicked);
        connect(ui->pushButton_qthreadTerminate,&QPushButton::clicked
                ,this,&Widget::onButtonQthreadTerminateClicked);
        connect(ui->pushButton_doSomthing,&QPushButton::clicked
                ,this,&Widget::onButtonQThreadDoSomthingClicked);
        //心跳的关联
        connect(&m_heart,&QTimer::timeout,this,&Widget::heartTimeOut);
        m_heart.setInterval(100);
        //全局线程的创建
        //全局线程创建时可以把窗体指针作为父对象
        m_thread = new ThreadFromQThread(this);
        //关联线程的信号和槽
        connect(m_thread,&ThreadFromQThread::message
                ,this,&Widget::receiveMessage);//
        connect(m_thread,&ThreadFromQThread::progress
                ,this,&Widget::progress);
        connect(m_thread,&QThread::finished
                ,this,&Widget::onQThreadFinished);
        //UI心跳开始
        m_heart.start();
    }
    

    由于是全局存在的线程,因此在窗体创建时就创建线程,可以把线程的父对象设置为窗体,这时需要注意,别手动delete线程指针。用于你的QThread是在Qt的事件循环里面,手动delete会发生不可预料的意外。理论上所有QObject都不应该手动delete,如果没有多线程,手动delete可能不会发生问题,但是多线程情况下delete非常容易出问题,那是因为有可能你要删除的这个对象在Qt的事件循环里还排队,但你却已经在外面删除了它,这样程序会发生崩溃

    如果你确实要删除,请参阅void QObject::deleteLater () [slot]这个槽,这个槽非常有用,尤其是对局部线程来说。后面会经常用到它用于安全的结束线程。

    在需要启动线程的地方调用start函数即可启动线程。

    void Widget::onButtonQThreadClicked()
    {
        ui->progressBar->setValue(0);
        if(m_thread->isRunning())
        {
            return;
        }
        m_thread->start();
    }
    

    如果线程已经运行,你重复调用start其实是不会进行任何处理。

    一个全局线程就那么简单,要用的时候start一下就行。真正要注意的是如何在ui结束时把线程安全退出。

    在widget的析构函数应该这样写:

    Widget::~Widget()
    {
        qDebug() << "start destroy widget";
        m_thread->stopImmediately();
        m_thread->wait();
        delete ui;
        qDebug() << "end destroy widget";
    }
    

    这里要注意的是m_thread->wait();这一句,这一句是主线程等待子线程结束才能继续往下执行,这样能确保过程是单一往下进行的,也就是不会说子线程还没结束完,主线程就destrioy掉了(m_thread的父类是主线程窗口,主线程窗口如果没等子线程结束就destroy的话,会顺手把m_threaddelete这时就会奔溃了),因此wait的作用就是挂起,一直等到子线程结束。

    还有一种方法是让QThread自己删除自己,就是在new线程时,不指定父对象,通过绑定**void QObject::deleteLater () [slot]**槽让它自动释放。这样在widget析构时可以免去m_thread->wait();这句。

    2.4.2 如何启动一个局部线程(用完即释放的线程)

    启动一个局部线程(就是运行完自动删除的线程)方法和启动全局线程差不多,但要关联多一个槽函数,就是之前提到的**void QObject::deleteLater () [slot]**,这个槽函数是能安全释放线程资源的关键(直接delete thread指针不安全)。

    简单的例子如下:

    void Widget::onButtonQThreadRunLoaclClicked()
    {
        //局部线程的创建的创建
        ThreadFromQThread* thread = new ThreadFromQThread(NULL);//这里父对象指定为NULL
        connect(thread,&ThreadFromQThread::message
                ,this,&Widget::receiveMessage);
        connect(thread,&ThreadFromQThread::progress
                ,this,&Widget::progress);
        connect(thread,&QThread::finished
                ,this,&Widget::onQThreadFinished);
        connect(thread,&QThread::finished
                ,thread,&QObject::deleteLater);//线程结束后调用deleteLater来销毁分配的内存
        thread->start();
    }
    

    这个例子还是启动之前的线程,但不同的是:

    • new ThreadFromQThread(NULL);并没有给他指定父对象
    • connect(thread,&QThread::finished ,thread,&QObject::deleteLater);线程结束后调用deleteLater来销毁分配的内存。
      再线程运行完成,发射finished信号后会调用deleteLater函数,在确认消息循环中没有这个线程的对象后会销毁。

    但是要注意避免重复点按钮重复调用线程的情况,对于一些需求,线程开启后再点击按钮不会再重新生成线程,一直等到当前线程执行完才能再次点击按钮,这种情况很好处理,加个标记就可以实现,也一般比较少用。

    另外更多见的需求是,再次点击按钮,需要终结上次未执行完的线程,重新执行一个新线程。这种情况非常多见,例如一个普通的图片浏览器,都会有下一张图和上一张图这种按钮,浏览器加载图片一般都在线程里执行(否则点击超大图片时图片浏览器会类似卡死的状态),用户点击下一张图片时需要终止正在加载的当前图片,加载下一张图片。你不能要求客户要当前图片加载完才能加载下一张图片,这就几乎沦为单线程了。这时候,就需要终止当前线程,开辟新线程加载下一个图片。

    这时,上面的函数将会是大概这个样子的

    UI的头文件需要一个成员变量记录正在运行的线程

    private slots:
       void onLocalThreadDestroy(QObject* obj);
    private:
       QThread* m_currentRunLoaclThread;
    

    运行生成临时线程的函数将变为

    void Widget::onButtonQThreadRunLoaclClicked()
    {
        //局部线程的创建的创建
        if(m_currentRunLoaclThread)
        {
             m_currentRunLoaclThread->stopImmediately();
        }
        ThreadFromQThread* thread = new ThreadFromQThread(NULL);
        connect(thread,&ThreadFromQThread::message
                ,this,&Widget::receiveMessage);
        connect(thread,&ThreadFromQThread::progress
                ,this,&Widget::progress);
        connect(thread,&QThread::finished
                ,this,&Widget::onQThreadFinished);
        connect(thread,&QThread::finished
                ,thread,&QObject::deleteLater);//线程结束后调用deleteLater来销毁分配的内存
        connect(thread,&QObject::destroyed,this,&Widget::onLocalThreadDestroy);
        m_currentRunLoaclThread = thread;
        thread->start();
    }
    
    void Widget::onLocalThreadDestroy(QObject *obj)
    {
        if(m_currentRunLoaclThread == obj)
        {
            m_currentRunLoaclThread = NULL;
        }
    }
    

    这里用一个临时变量记录当前正在运行的局部线程,由于线程结束时会销毁自己,因此要通知主线程把这个保存线程指针的临时变量设置为NULL
    因此用到了QObject::destroyed信号,在线程对象析构时通知UI把m_currentRunLoaclThread设置为nullptr;

    3. 继承QThread的一些总结

    • QThread只有run函数是在新线程里的

    • QThread执行start函数之后,run函数还未运行完毕,再次start会出现什么后果?

    答案是:不会发生任何结果,QThread还是继续执行它的run函数,run函数不会被重新调用。虽然在线程未结束时调用start不会出现什么结果,但为了谨慎起见,还是建议在start之前进行判断:

    void Widget::onButtonQThreadClicked()
    {
        ui->progressBar->setValue(0);
        if(m_thread->isRunning())
        {
            return;
        }
        m_thread->start();
    }
    

    这种调用方法估计了解过QThread的都知道

    • 在线程运行过程调用quit函数有什么效果

    答案是:不会发生任何效果,QThread不会因为你调用quit函数而退出正在运行到一半的run,正确退出线程的方法上面有介绍。那quit到底有什么用的呢,这要到下篇才能看出它的作用。使用moveToThread方法执行多线程时,这个函数将有大作用。

    • 程序在退出时要判断各线程是否已经退出,没退出的应该让它终止
      如果不进行判断,很可能程序退出时会崩溃。如果线程的父对象是窗口对象,那么在窗体的析构函数中,还需要调用wait函数等待线程完全结束再进行下面的析构。

    • 善用QObject::deleteLaterQObject::destroyed来进行内存管理
      由于多线程环境你不可预料下一步是哪个语句执行,因此,加锁和自动删除是很有用的工具,加锁是通过效率换取安全,用Qt的信号槽系统可以更有效的处理这些问题。

    使用QObject实现多线的方法见:http://blog.csdn.net/czyt1988/article/details/71194457

    展开全文
  • 关于Django多进程多线程详解

    万次阅读 2019-05-08 15:01:34
    文章目录Django 多线程#1 环境#2 Django原生单线程#3 Django server 默认多线程#3.1 测试#4 使用 uWSGI 服务器启动 django#4.1 启动 uWSGI#4.2 给uWSGI加进程数#5 总结关于django的多线程问题 Django 多线程 #1 环境...

    Django 多线程

    #1 环境

    Python3.7.3
    Django==2.0.7
    uWSGI==2.0.18
    

    #2 Django原生单线程

    • #1

    django 原生为单线程序,当第一个请求没有完成时,第二个请求辉阻塞,知道第一个请求完成,第二个请求才会执行。

    • #2

    Django就没有用异步,通过线程来实现并发,这也是WSGI普遍的做法,跟tornado不是一个概念


    #3 Django server 默认多线程

    官方文档解释django自带的server默认是多线程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMyHNqj4-1591867681155)(https://raw.githubusercontent.com/Coxhuang/yosoro/master/20190507225848-image.png)]

    #3.1 测试

    • python3 manage.py runserver 0.0.0.0:8000

    django开两个接口,第一个接口sleep(20),另一个接口不做延时处理(大概耗时几毫秒)

    先请求第一个接口,紧接着请求第二个接口,第二个接口返回数据,第一个接口20秒之后返回数据

    证明django的server是默认多线程

    • python3 manage.py runserver 0.0.0.0:8000 --nothreading

    重复上述步骤


    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uMJ9iYfh-1591867681157)(https://raw.githubusercontent.com/Coxhuang/yosoro/master/20190507230747-image.png)]


    #4 使用 uWSGI 服务器启动 django

    #4.1 启动 uWSGI

    # 在django项目目录下 Demo工程名
    uwsgi --http 0.0.0.0:8000 --file Demo/wsgi.py
    

    经过上述的步骤测试,发现在这种情况下启动django项目,uWSGI也是单线程,访问接口需要"排队"

    不给uWSGI加进程,uWSGI默认是单进程单线程

    #4.2 给uWSGI加进程数

    uwsgi --http 0.0.0.0:8000 --file Demo/wsgi.py --processes 4 --threads 2
    
    # processes: 进程数 # processes 和 workers 一样的效果
    # threads : 每个进程开的线程数 
    

    经过测试,接口可以"同时"访问,uWSGI提供多线程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DpNUmPNA-1591867681159)(https://raw.githubusercontent.com/Coxhuang/yosoro/master/20190507233529-image.png)]


    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBjZbSUv-1591867681160)(https://raw.githubusercontent.com/Coxhuang/yosoro/master/20190507233629-image.png)]


    #5 总结关于django的多线程问题

    • Python因为GIL的存在,在一个进程中,只允许一个线程工作,导致单进程多线程无法利用多核
    • 多进程的线程之间不存在抢GIL的情况,每个进程有一个自己的线程锁,多进程多GIL
    • 单进程多线程的python应用可以实现并发,但是不存在并行
    • 多进程的多处理器的python应用可能存在并行,至于并发还是并行,有操作系统决定,如果分配单处理器处理多进程,那就是并行,如果分配给多处理器那就是并行
    • Nginx+uWSGI可以实现python高并发


    展开全文
  • Java 通过 Thread 实现心跳线程

    千次阅读 2018-11-14 18:53:57
    在 Java 开发中,可以通过多线程工具解决这一问题。用来实现这一功能的进程,有时也被称为“心跳进程”。 实现 创建文件 HeartBeating.java,代码如下: public class HeartBeating { public static void start...

    Java 学习笔记(02)

    mywang88


    简介

    后台运行的程序,有时会被需要定时输出信号,已确定程序运行正常。

    在 Java 开发中,可以通过多线程工具解决这一问题。用来实现这一功能的进程,有时也被称为“心跳进程”。

    实现

    创建文件 HeartBeating.java,代码如下:

    public class HeartBeating {
        public static void startBeating() {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    while(true) {
                        System.out.println("I am alive!");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            //
                        }
                    }
                }
            };
            Thread t = new Thread(r);
            t.start();
        }
    }
    

    HeartBeating 类提供了静态方法 startBeating()。调用这个方法,可以创建一个新的线程,每隔 1 秒向屏幕输出字符串 “I am alive” 。

    启动类:

    public class StartClass {
        public static void main(String[] args){
            HeartBeating.startBeating();
        }
    }
    

    这样,就完成了一个心跳线程。同样,这也适用于创建其它线程。

    展开全文
  • ue4的多线程如何实现

    千次阅读 2018-11-20 13:30:57
    ue4的多线程如何实现讲道理实例创建c++工程新建一个FunctionLibrary在functionlibrary里编写三个函数,并且在cpp里面加以实现再创建一个空类对该项目在vs 上进行生成创建测试用例结论 讲道理 一般要使用多线程技术的...
  • 多线程网络通信

    千次阅读 2010-10-27 22:49:00
    最近写一个多线程的网络通信DLL,原来认为自己的网络通信这一方面水平即使不是杠杠滴,也得当当滴,至不成也得忽忽滴。结果真得一写起来,发现很多东西都吃不透,都懂,都明白大其概,但真正应用哪种更好,哪...
  • 这篇文章主要介绍了python多线程并发及测试框架案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1、循环创建多个线程,并通过循环启动执行 import ...
  • python——多线程thread

    2013-07-31 23:50:32
    python通过thread模块支持多线程,语法也很简洁,现在通过一个实例来看一下python中的多线程: import thread import time isRunning=False timer_hour=23 timer_min=44 def another_thread(): print '[another_...
  • 【Kafka笔记】5.Kafka 多线程消费消息

    千次阅读 2019-11-24 22:45:45
    Kafka多线程消费理解 Kafka Java Consumer设计 Kafka Java Consumer采用的是单线程的设计。其入口类KafkaConsumer是一个双线程的设计,即用户主线程和心跳线程。 用户主线程,指的是启动Consumer应用程序main方法的...
  • 关于java多线程

    2014-12-06 23:27:43
    对于JAVA多线程的应用非常广泛,现在的系统没有多线程几乎什么也做不了,很多时候我们在何种场合如何应用多线程成为一种首先需要选择的问题,另外关于java多线程的知识也是非常的多,本文中先介绍和说明一些常用的,...
  • 多线程服务器

    千次阅读 2010-07-22 10:30:00
    多线程服务器的常用编程模型 陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice 2010 Feb 12 本文 PDF 版下载: http://files.cppblog.com/Solstice/multithreaded_server.pdf 本文主要讲我...
  • 多线程编程模式

    千次阅读 2016-01-27 22:09:25
    多线程服务器的常用编程模型 本文主要讲我个人在多线程开发方面的一些粗浅经验。总结了一两种常用的线程模型,归纳了进程间通讯与线程同步的最佳实践,以期用简单规范的方式开发多线程程序。 文中的“多线程...
  • 笔者初学 Python,在编程中,遇到了多线程的问题,即需要一个程序中的几部分同时运行。 例如:给一个后台程序写一个“心跳进程”,定时输出信号,以确认程序正常运行。 在网上搜索了下发现有不少帖子,但总感觉帖子...
  • 对于JAVA多线程的应用非常广泛,现在的系统没有多线程几乎什么也做不了,很多时候我们在何种场合如何应用多线程成为一种首先需要选择的问题,另外关于java多线程的知识也是非常的多,本文中先介绍和说明一些常用的,...
  • 高并发服务器编程之多进程并发服务器 一、多线程服务器分析:多进程并发与多线程并发实现过程差不多,只是多线程的同步、资源回收与多进程还是有很多区别的。多进程不需要记录子进程的信息,而多线程需要记录。 ...
  • 《Java多线程编程核心技术》总结笔记
  • 包括TCP服务器客户端多线程通信程序(用select实现),基于Visual Studio2017实现,已编译测试过,用到的朋友记得关闭SDL检查
  • Kafka 多线程开发消费实例 Kafka Java Consumer 设计原理 谈到 Java Consumer API,最重要的当属它的入口类 KafkaConsumer 了。我们说 KafkaConsumer 是单线程的设计,严格来说这是不准确的。因为,从 Kafka 0.10....
  • Qt创建多线程的两种方法

    万次阅读 多人点赞 2017-12-25 15:33:54
    Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。 Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。两种方法...
  • 从 Kafka 0.10.1.0 版本开始,KafkaConsumer 就变为了双线程的设计,即用户主线程和心跳线程。 所谓用户主线程,就是你启动 Consumer 应用程序 main 方法的那个线程,而新引入的心跳线程(Heartbeat Thread)只...
  • 多线程服务器的常用编程模型

    千次阅读 2012-07-20 18:22:06
    多线程服务器的常用编程模型 分类: c++ 多线程 2010-02-12 17:07 29469人阅读 评论(27) 收藏 举报   多线程服务器的常用编程模型   陈硕 (giantchen_AT_gmail) Blog.csdn.net/...
  • 把Client做成了一个类,实现了发送、接收接口,自动重连,心跳检测,python多线程实现同时执行两个while循环
  • 多线程服务器模型

    千次阅读 2013-04-10 16:20:19
    总结了一两种常用的线程模型,归纳了进程间通讯与线程同步的最佳实践,以期用简单规范的方式开发多线程程序。 文中的“多线程服务器”是指运行在 Linux 操作系统上的独占式网络应用程序。硬件平台为 Intel x64 ...
  • 多线程的网络通信

    千次阅读 2011-10-09 20:08:46
    最近写一个多线程的网络通信DLL,原来认为自己的网络通信这一方面水平即使不是杠杠滴,也得当当滴,至不成也得忽忽滴。结果真得一写起来,发现很多东西都吃不透,都懂,都明白大其概,但真正应用哪种更好,哪种更...
  • socket通信的demo,采用了多线程收发,别的如题

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,865
精华内容 17,546
关键字:

多线程心跳