精华内容
下载资源
问答
  • 多线程】获取多线程执行结果
    千次阅读
    2020-04-02 12:52:12

    1. 观察者模式结合多线程实现获取线程执行结果

    在多线程开发中,我们没办法获取线程执行完成后的结果,但是当我们利用观察者模式的时候我们就可以获取到多线程的执行结果

    2.具体实现

    假设目前有一批id需要查询并需要获得查询结果, 利用多线程结合设计模式实现

    2.1 LifeCycleListener

    public interface LifeCycleListener {
        void onEvent(ObserverableRunnable.RunnableEvent event);
    }
    

    2.2 ObserverableRunnable

    
    public abstract class ObserverableRunnable implements Runnable{
    
        /**
         * 监听这
         */
        final protected LifeCycleListener listener;
    
        public ObserverableRunnable(LifeCycleListener listener) {
            this.listener = listener;
        }
    
    
        public void notifyChange(final RunnableEvent event) {
            listener.onEvent(event);
        }
    
        public enum RunnableState {
            RUNNING, ERROR, DONE;
        }
    
        /**
         * 内部类尽量都是静态的,不然作为成员对象每次创建对象都会复制
         */
        public static class RunnableEvent {
    
            private final RunnableState state;
            private final Thread thread;
            private final Throwable cause;
    
    
            public RunnableEvent(RunnableState state, Thread thread, Throwable cause) {
                this.state = state;
                this.thread = thread;
                this.cause = cause;
            }
    
            public RunnableState getState() {
                return state;
            }
    
            public Thread getThread() {
                return thread;
            }
    
            public Throwable getCause() {
                return cause;
            }
        }
    }
    
    

    2.3 ThreadLifeCycleObserver

    package com.gy.observer;
    
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.IntStream;
    
    /**
     * 模拟当前类使用多线程需要去处理一批id,并拿到结果
     * 1. 创建处理方法并通过ObserverableRunnable去执行任务
     * 2. 当ObserverableRunnable吧任务执行完成后提醒当前类做完了
     * 3. 拿到返回值处理
     */
    public class ThreadLifeCycleObserver implements LifeCycleListener {
    
    
        private Object LOCK = new Object();
    
        private void concurrentQuery(List<Integer> ids) {
            if (ids.size() == 0 && ids.isEmpty())
                return;
    
            ids.stream().forEach((id)->{
                new Thread(new ObserverableRunnable(this) {
                    @Override
                    public void run() {
                        try {
                            notifyChange(new RunnableEvent(RunnableState.RUNNING,  Thread.currentThread(), null));
                            System.out.printf("query for id = %s\n", id);
                            Thread.sleep(1000);
                            notifyChange(new RunnableEvent(RunnableState.DONE,  Thread.currentThread(), null));
                        } catch (InterruptedException e) {
                            notifyChange(new RunnableEvent(RunnableState.ERROR,  Thread.currentThread(), e));
                        }
                    }
                }).start();
            });
        }
    
    
    
        /**
         * 这个方法可能是多个线程在回调所以需要使用同步锁
         * @param event
         */
        @Override
        public void onEvent(ObserverableRunnable.RunnableEvent event) {
            synchronized (LOCK) {
                System.out.println(event.getThread().getName() + " == " + event.getState());
            }
        }
    
    
        public static void main(String[] args) {
            ThreadLifeCycleObserver threadLifeCycleObserver = new ThreadLifeCycleObserver();
            threadLifeCycleObserver.concurrentQuery(Arrays.asList(1, 2, 3));
        }
    }
    
    
    更多相关内容
  • java多线程设计模式详解(PDF及源码)

    热门讨论 2014-05-07 20:54:32
    java多线程设计模式详解 (PDF及源码)
  • Java多线程设计模式_清晰完整PDF版

    千次下载 热门讨论 2015-04-22 17:37:18
    Java多线程设计模式_清晰完整PDF版,带有源码。绝对的清晰版,并且内容完整。
  • Win32多线程程序设计--源代码

    热门讨论 2012-04-22 17:09:08
     在《Win32多线程程序设计》这本书中,Jim Beveridge和Robert Wiener告诉你什么时机、什么地点、什么方法可以使用多线程。  本书主题包括:  ● Internet开发范例,包括ISAPI和WinSock。  ● 如何在服务器中...
  • POSIX多线程程序设计.pdf

    千次下载 热门讨论 2012-12-02 20:03:40
    《POSIX多线程程序设计》深入描述了IEEE的开放系统接口标准——POSIX线程,通常称为Pthreads标准。本书首先解释了线程的基本概念,包括异步编程、线程的生命周期和同步机制;然后讨论了一些高级话题,包括属性对象、...
  • POSIX多线程程序设计中文版.pdf

    热门讨论 2009-10-13 14:26:47
    POSIX多线程程序设计(Programming with POSIX Threads) 作者:(美)布腾霍夫 (Butenhof David R.) 译者:于磊 曾刚 出版社:中国电力出版社 出版日期:2003年4月 页数:321 装帧:平装 开本:开 版次:1 内容简介: ...
  • 真的还有必要学习JAVA多线程吗?

    千次阅读 多人点赞 2022-02-18 21:02:28
    JAVA多线程是不是真的没必要再学了?

    🔎这里是JAVA多线程加油站
    👍如果对你有帮助,给博主一个免费的点赞以示鼓励
    欢迎各位🔎点赞👍评论收藏⭐️

    一、忘掉那该死的并行

    Linus Torvalds是一个传奇式的人物(图1.1),是他给出了Linux的原型,并一直致力于推广和发展Linux系统。1991年,他首先在网络上发布了Linux源码,从此Linux 迅速崛起壮大,成为目前使用最广泛的操作系统之一。
    在这里插入图片描述

    • 自2002年起,Linus就决定使用BitKeeper作为Linux内核开发的版本控制工具,以此来维护Linux的内核源码。BitKeeper
      是一套分布式版本控制软件,它是一套商用系统,由BitMover公司开发。2005年,BitKeeper宣称发现
      Linux内核开发人员使用逆向工程来试图解析BitKeeper 内部协议。因此,决定向Linus 收回
      BitKeeper授权。虽然Linux核心团队与BitMover公司进行了协商,但是仍无法解决他们之间的分歧。因此,Linus
      决定自行研发版本控制工具来代替BitKeeper。于是,Git诞生了。
    • 如果你正在使用Git,那么我相信你一定会为Git的魅力所征服;如果你还没有了解过Git,那么我强烈建议你关注一下这款优秀的产品。
    • 而正是这位传奇人物,给目前红红火火的并行计算泼了一大盆冷水。那么,并行计算究竟应该何去何从呢?

    在 Linus的发言中这么说道:

    Where the hell do you envision that those magical parallel algorithms would be used?
    The only place where parallelism matters is in graphics or on the server side,where we alreadylargely have it. Pushing it anywhere else is just pointless.
    So the whole argument that people should parallelise their code is fundamentally flawed. It restson incorrect assumptions.It’s a fad that has been going on too long.

    需要有多么奇葩的想象力才能想象出并行计算的用武之地?
    并行计算只能在图像处理和服务端编程两个领域使用,并且它在这两个领域确实有着大量广泛的使用。但是在其他任何地方,并行计算毫无建树!
    因此,人们争论是否应该将代码并行化是一个本质上的错误。这完全基于一个错误的假设。“并行”是一个早该结束的时髦用语。

    • 看了这段较为完整的表述,大家应该对Linus 的观点有所感触,我对此也表示赞同。与串行程序不同,并行程序的设计和实现异常复杂,不仅体现在程序的功能分离上,多线程间的协调性、乱序性都会成为程序正确执行的障碍。只要你稍不留神,就会失之毫厘,谬以千里!混乱的程序难以阅读、难以理解,更难以调试。所谓并行,也就是把简单问题复杂化的典型。因此,只有“疯子”才会叫嚣并行就是未来(Thecrazies talking about scaling tohundreds of cores are just that -crazy)。
    • 但是,Linus也提出了两个特例,那就是图像处理和服务端程序是可以也需要使用并行技术的。仔细想想,为什么图像处理和服务端程序是特例呢?
    • 和用户终端程序不同,图像处理往往拥有极大的计算量。一张1024×768像素的图片,包含多达78万6千多个像素。即使将所有的像素遍历一遍,也得花不少时间。更何况,图像处理涉及大量的矩阵计算。矩阵的规模和数量都会非常大。因为如此密集的计算,很有可能超过单核CPU的计算能力,所以自然需要引入多核计算了。
    • 而服务端程序与一般的用户终端程序相比,一方面,服务端程序需要承受很大的用户访问压力。根据淘宝的数据,它在“双11”一天,支付宝核心数据库集群处理了41亿个事务,执行285亿次SQL,生成15TB日志,访问1931亿次内存数据块,发生13亿个物理读。如此密集的访问,恐怕任何一台单核计算机都难以胜任,因此,并行程序也就自然成了唯一的出路。另一方面,服务端程序往往会比用户终端程序拥有更复杂的业务模型。面对复杂业务模型,并行程序会比串行程序更容易适应业务需求,更容易模拟我们的现实世界。毕竟,我们的世界本质上是并行的。比如,当你开开心心去上学的时候,妈妈可能在家里忙着家务,爸爸在外打工赚钱,一家人其乐融融。如果有一天,你需要使用你的计算机来模拟这个场景,你会怎么做呢?如果你就在一个线程里,既做了你自己,又做了妈妈,又做了爸爸,显然这不是一种好的解决方案。但如果你使用三个线程,分别模拟这三个人,一切看起来那么自然,而且容易被人理解。
    • 再举一个专业点的例子,比如基础平台Java虚拟机,虚拟机除了要执行main函数主线程外,还需要做JIT
      编译,需要做垃圾回收。无论是main
      函数、JIT编译还是垃圾回收,在虚拟机内部都是一个单独的线程。是什么使得虚拟机的研发人员这么做的呢?显然,这是因为建模的需要。因为这里的每一个任务都是相对独立的。我们不应该将没有关联的业务代码拼凑在一起,分离为不同的线程更容易理解和维护。因此,使用并行也不完全是出于性能的考虑,而有时候,我们会很自然地那么做。

    二、可怕的现实:摩尔定律的失效

    • 摩尔定律是由英特尔创始人之一戈登·摩尔提出来的,其内容为:集成电路上可容纳的电晶体(晶体管)数目,约每隔24个月便会增加一倍。经常被引用的“18个月”,是由英特尔首席执行官大卫·豪斯所说:预计18个月会将芯片的性能提高一倍(即更多的晶体管使其更快)。
    • 说得直白点,就是每18个月到24个月,我们的计算机性能就能翻一番。
    • 反过来说,就是每过18个月到24个月,你在未来用一半的价钱就能买到和现在性能相同的计算设备了。这听起来是一件多么激动人心的事情呀!
    • 但是,摩尔定律并不是一种自然法则或者物理定律,它只是基于人为观测数据对未来的预测。按照这种速度,我们的计算能力将会按照指数速度增长,用不了多久,我们的计算能力就能超越“上帝”了!畅想未来,基于强劲的超级计算机,我们甚至可以模拟整个宇宙。
    • 摩尔定律的有效性已经超过半个世纪了,然而,在2004年,Intel宣布将4GHz芯片的发布时间推迟到2005年,在2004年秋季,Intel宣布彻底取消4GHz计划(如图1.2所示)。
    • 是什么迫使世界顶级的科技巨头放弃4GHz的研发呢?显然,就目前的硅电路而言,很有可能已经走到了头。我们的制造工艺已经精确到了纳米了。1纳米是10°米,也就是十亿分之一米。这已经是一个相当小的数字了。就目前的科技水平而言,如果无法在物质分子层面以下进行工作,那么也许4GHz的芯片就已经接近理论极限了。因为即使一个水分子,它的直径也有0.4纳米。再往下发展就显得有些困难。当然,如果我们使用完全不同的计算理论或者芯片生成工艺,也许会有本质的突破,但目前还没有看到这种技术被大规模使用的可能。

    在这里插入图片描述
    因此,摩尔定律在CPU的计算性能上可能已经失效。虽然,现在Intel已经研制出了4GHz芯片,但可以看到,在近10年的发展中,CPU主频的提升已经明显遇到了一些暂时不可逾越的瓶颈。

    三、柳暗花明:不断地前进

    • 虽然CPU的性能已经几近止步,长达半个世纪的摩尔定律轰然倒地,但是这依然没有阻挡科学家和工程师们带领我们不断向前的脚步。
      从2005年开始,我们已经不再追求单核的计算速度,而着迷于研究如何将多个独立的计算单元整合到单独的CPU中,也就是我们所说的多核CPU。短短十几年的发展,家用型CPU,比如Intel
      i7就可以拥有4核心,甚至8核心。而专业服务器则通常可以配有几个独立的CPU,每一个CPU都拥有多达8个甚至更多的内核。从整体上看,专业服务器的内核总数甚至可以达到几百个。
    • 非常令人激动,摩尔定律在另外一个侧面又生效了。根据这个定律,我们可以预测,每过18个月到24个月,CPU的核心数就会翻一番。用不了多久,拥有几十甚至上百个CPU内核的芯片就能进入千家万户。
    • 顶级计算机科学家唐纳德·尔文·克努斯(Donald Ervin
      Knuth),如此评价这种情况:在我看来,这种现象(并发)或多或少是由于硬件设计者已经无计可施导致的,他们将摩尔定律失效的责任推给软件开发者。
    • 唐纳德(如图1.3所示)是计算机巨著《计算机程序设计艺术》的作者。《美国科学家》杂志曾将该书与爱因斯坦的《相对论》、狄拉克的《量子力学》和理查·费曼的《量子电动力学》等书并列为20世纪最重要的12本物理科学类专论书之一。

    在这里插入图片描述

    四、光明或是黑暗

    • 根据唐纳德的观点,摩尔定律本应该由硬件开发人员维持。但是,很不幸,硬件工程师似乎已经无计可施了。为了继续保持性能的高速发展,硬件工程师破天荒地想出了将多个CPU内核塞进一个CPU里的奇妙想法。由此,并行计算就被非常自然地推广开来,随之而来的问题也层出不穷,程序员的黑暗时期也随之到来。简化的硬件设计方案必然带来软件设计的复杂性。换句话说,软件工程师正在为硬件工程师无法完成的工作负责,因此,也就有了唐纳德的“他们将摩尔定律失效的责任推给了软件开发者”的说法。
    • 所以,如何让多个CPU有效并且正确地工作也就成了一门技术,甚至是很大的学问。比如,多线程间如何保证线程安全,如何正确理解线程间的无序性、可见性,如何尽可能地设计并行程序,如何将串行程序改造为并行程序。而对并行计算的研究,也就是希望给这片黑暗带来光明。

    本文参考多线程高并发程序设计,非常棒的一本书,推荐xdm学习

    展开全文
  • PyQt多线程将使用QThread函数,QThread是Qt的线程类中最核心的底层类。

    有时候在项目开发时,经常会遇到一些耗时操作导致界面迟缓卡顿,为了解决这个问题,可以创建多线程,使用主线程更新界面使用子线程实时处理数据,最后将结果显示到界面上。

    PyQt多线程将使用QThread函数,QThread是Qt的线程类中最核心的底层类。由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码要使用的QThread开始一个线程,可以创建它的一个子类,然后覆盖其它QThread.run()函数。

    PyQt的线程使用非常简单:建立一个自定义的类(如thread),自我继承自QThread ,并实现其run()方法即可。

    在使用线程时可以直接得到Thread实例,调用其start()函数即可启动线程,线程启动之后,会自动调用其实现的run()函数,该方法就是线程的执行函数业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了。

    QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执行一段代码进行资源的初始化和释放操作,更灵活的使用方法是:在自定义的QThread实例中自定义信号,并将信号连接到指定的槽函数,当满足一定的业务条件时发射此信号。

    QThread类中的常用方法如下表所示:

    方法描述
    start()启动线程
    wait()

    阻止线程,直到满足如下条件之一。

    1、与此QThread对象关联的线程已完成执行(即从run返回时),如果线程完成执行,此函数返回True,如果线程尚未启动,也返回True;

    2、等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False

    sleep()强制当前线程睡眠多少秒

    QThread类中的常用信号如下表所示:

    信号描述
    started在开始执行run函数之前,从相关线程发射此信号
    finished当程序完成业务逻辑时,从相关线程发射此信号

    QThread的简单使用如以下代码所示:

    class Thread(QThread):     #继承QThread
          def __init__(self):
              super(Thread,self).__init__()
    
          def run(self):
              #线程相关的代码
              passs
    
    #创建一个新的线程
    thread=Thread()
    #启动线程
    thread.start()

    通过一个小案例来了解QThread多线程的使用吧。本例中,定义了一个后台线程类BackendThread来模拟后台耗时操作,在这个线程类中定义了信号update_date。使用 BackendThread 线程类在后台处理数据,每秒发射一次自定义信号update_date。

    在初始化窗口界面时,定义后台线程类BackendThread,并把线程类的信号update_date连接到槽函数handleDisplay()。这样后台线程每发射一次信号,就可以把最新的时间值实时显示在前台窗口的QLineEdit文本对话框中。

    实现代码如下所示:

    from PyQt5.QtCore import QThread ,  pyqtSignal,  QDateTime 
    from PyQt5.QtWidgets import QApplication,  QDialog,  QLineEdit
    import time
    import sys
    
    class BackendThread(QThread):
        # 通过类成员对象定义信号对象  
    	update_date = pyqtSignal(str)
    	
        # 处理要做的业务逻辑
    	def run(self):
    		while True:
    			data = QDateTime.currentDateTime()
    			currTime = data.toString("yyyy-MM-dd hh:mm:ss")
    			self.update_date.emit( str(currTime) )
    			time.sleep(1)
    
    class Window(QDialog):
    	def __init__(self):
    		QDialog.__init__(self)
    		self.setWindowTitle('PyQt5界面实时更新例子')
    		self.resize(400, 100)
    		self.input = QLineEdit(self)
    		self.input.resize(400, 100)
    		self.initUI()
    
    	def initUI(self):
            # 创建线程  
    		self.backend = BackendThread()
            # 连接信号 
    		self.backend.update_date.connect(self.handleDisplay)
            # 开始线程  
    		self.backend.start()
        
        #将当前时间输出到文本框
    	def handleDisplay(self, data):
    		self.input.setText(data)
    
    if __name__ == '__main__':
    	app = QApplication(sys.argv)
    	win = Window()
    	win.show() 
    	sys.exit(app.exec_())

    运行效果如下所示:


    参考资料、拓展学习:

    多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)

    PyQt5高级界面控件之QThread(十二) 

    展开全文
  • Windows下基于socket多线程并发通信的实现

    千次下载 热门讨论 2015-04-07 15:06:06
    本文介绍了在Windows 操作系统下基于TCP/IP 协议Socket 套接口的通信机制以及多线程编程知识与技巧,并给出多线程方式实现多用户与服务端(C/S)并发通信模型的详细算法,最后展现了用C++编写的多用户与服务器通信的...
  • 爬虫 第五讲 多线程爬虫

    万次阅读 2021-04-29 15:30:00
    爬虫 第五讲 多线程爬虫 一、多线程 1.多线程基本介绍 有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的。 程序中模拟多任务 import time def sing(): for ...

    爬虫 第五讲 多线程爬虫

    一、多线程

    1.多线程基本介绍

    有很多的生活场景中的事情是同时进行的,比如一边做饭,一边跟别人聊天,一边听歌。

    示例1

    import time
    
    def sing():
        for i in range(3):
            print("正在唱歌...%d" % i)
            time.sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d" % i)
            time.sleep(1)
    
    if __name__ == '__main__':
        sing()
        dance()
    '''
    正在唱歌...0
    正在唱歌...1
    正在唱歌...2
    正在跳舞...0
    正在跳舞...1
    正在跳舞...2
    '''
    

    示例2

    import time
    import threading
    def sing():
        for i in range(3):
            print("正在唱歌...%d" % i)
            time.sleep(1)
    
    def dance():
        for i in range(3):
            print("正在跳舞...%d" % i)
            time.sleep(1)
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=sing)
        t2 = threading.Thread(target=dance)
        t1.start()
        t2.start()
    '''
    正在唱歌...0
    正在跳舞...0
    正在唱歌...1
    正在跳舞...1
    正在唱歌...2
    正在跳舞...2
    '''
    

    2.主线程和子线程的执行关系

    主线程会等待子线程结束之后在结束

    import threading
    import time
    
    def demo():
        for i in range(5):
            print('hello 子线程')  # 子线程
            time.sleep(1)
    
    if __name__ == '__main__':
        # 参数1  group 线程组 None
        # 参数2  target 要执行的方法
        # 参数3  name  线程名
        t = threading.Thread(target=demo)
        # 主线程会等待子线程结束之后再结束
        t.start()  # 创建并开启线程
        print(1)
    
    '''
    hello 子线程
    1
    hello 子线程
    hello 子线程
    hello 子线程
    hello 子线程'''
    

    join() 等待子线程结束之后,主线程继续执行

    import threading
    import time
    
    def demo():
        for i in range(5):
            print('hello 子线程')  # 子线程
            time.sleep(1)
    
    if __name__ == '__main__':
        # 参数1  group 线程组 None
        # 参数2  target 要执行的方法
        # 参数3  name  线程名
        t = threading.Thread(target=demo)
        # 主线程会等待子线程结束之后再结束
        t.start()  # 开启线程
        # 等待子进程结束之后,主线程再继续执行
        t.join()
        print(1)
    '''
    hello 子线程
    hello 子线程
    hello 子线程
    hello 子线程
    hello 子线程
    1
    '''
    

    setDaemon() 守护线程,不会等待子线程结束

    import threading
    import time
    def demo():
        for i in range(5):
            print('hello 子线程')  # 子线程
            time.sleep(1)
    
    if __name__ == '__main__':
        # 参数1  group 线程组 None
        # 参数2  target 要执行的方法
        # 参数3  name  线程名
        t = threading.Thread(target=demo)
        # 守护线程,不会等待子线程结束
        t.setDaemon(True)
        # 主线程会等待子线程结束之后再结束
        t.start()  # 开启线程
        print(1)
    '''hello 子线程1'''
    
    import threading
    import time
    def run(num):
        print("子线程(%s)开始" % (threading.current_thread().name))
        # 实现线程的功能
        time.sleep(2)
        print("打印", num)
        time.sleep(2)
        print("子线程(%s)结束" % (threading.current_thread().name))
    
    if __name__ == "__main__":
        # 任何进程默认就会启动一个线程,称为主线程,主线程可以启动新的子线程
        # 打印当前线程的名称,current_thread():返回当前线程的实例
        print("主线程(%s)启动" % (threading.current_thread().name))
        # t.start()创建并启动子线程
        t = threading.Thread(target=run, name="runThread", args=(1,))  # 如果不写 name="runThread",(Thread-1)排下去
        t.start()
        t.join()  # 等待线程结束
        print("主线程(%s)结束" % (threading.current_thread().name))
    '''
    主线程(MainThread)启动
    子线程(runThread)开始
    打印 1
    子线程(runThread)结束
    主线程(MainThread)结束'''
    
    import threading
    import time
    
    def demo():
        # 子线程
        print("hello man")
        time.sleep(1)
    
    if __name__ == "__main__":
        for i in range(5):
            t = threading.Thread(target=demo)
            t.start()
    '''
    hello man
    hello man
    hello man
    hello man
    hello man'''
    

    3.查看线程数量

    threading.enumerate()	查看当前线程的数量
    
    # threading.enumerate  列出活着的线程
    import time
    import threading
    
    def demo1():
        for i in range(5):
            print('demo1-----%d' % i)
            time.sleep(1)
    
    def demo2():
        for i in range(10):
            print('demo2-----%d' % i)
            time.sleep(1)
    
    def main():
        t1 = threading.Thread(target=demo1)
        t2 = threading.Thread(target=demo2)
        t1.start()
        t2.start()
        while True:
            print(threading.enumerate())
            if len(threading.enumerate()) <=1:
                break
            time.sleep(1)
    
    if __name__ == '__main__':
        main()
    '''
    demo1-----0
    demo2-----0[<_MainThread(MainThread, started 11424)>, <Thread(Thread-1, started 11504)>, <Thread(Thread-2, started 11204)>]
    
    demo1-----1
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-1, started 11504)>, <Thread(Thread-2, started 11204)>]
    demo2-----1
    demo1-----2
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-1, started 11504)>, <Thread(Thread-2, started 11204)>]
    demo2-----2
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-1, started 11504)>, <Thread(Thread-2, started 11204)>]
    demo2-----3
    demo1-----3
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-1, started 11504)>, <Thread(Thread-2, started 11204)>]demo2-----4
    
    demo1-----4
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]
    demo2-----5
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]demo2-----6
    
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]
    demo2-----7
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]
    demo2-----8
    demo2-----9[<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]
    
    [<_MainThread(MainThread, started 11424)>, <Thread(Thread-2, started 11204)>]
    [<_MainThread(MainThread, started 11424)>]
    '''
    

    4.验证子线程的执行与创建

    当调用Thread的时候,不会创建线程。
    当调用Thread创建出来的实例对象的start方法的时候,才会创建线程以及开始运行这个线程。

    import threading
    import time
    def demo():
        for i in range(5):
            print('demo-----%d'%i)
            time.sleep(1)
    
    def main():
        print(threading.enumerate())  # 一个主线程
        t1 = threading.Thread(target=demo)  # 创建? 证明它不是创建线程
        print(threading.enumerate())  # 一个主线程
        t1.start()  # 创建线程并启动线程
        print(threading.enumerate())  # 有2个线程,一个是主线程,一个是子线程
    
    
    if __name__ == '__main__':
        main()
    '''
    [<_MainThread(MainThread, started 21084)>]
    [<_MainThread(MainThread, started 21084)>]
    demo-----0[<_MainThread(MainThread, started 21084)>, <Thread(Thread-1, started 11688)>]
    
    demo-----1
    demo-----2
    demo-----3
    demo-----4'''
    

    5.继承Thread类创建线程

    示例1

    import threading
    import time
    
    
    class A(threading.Thread):
    
        def __init__(self, name):
            super().__init__(name=name)
    
        def run(self):
            for i in range(5):
                print(i)
    
    
    if __name__ == "__main__":
        t = A('test_name')
        t.start()
    '''
    0
    1
    2
    3
    4'''
    

    示例2

    import threading
    import time
    class A(threading.Thread):
        def run(self):
            for i in range(4):
                print(i)
    
    
    if __name__ == '__main__':
        a = A()
        print(threading.enumerate())
        a.start()
        print(threading.enumerate())
        time.sleep(5)
        print(123)
        print(threading.enumerate())
    '''
    [<_MainThread(MainThread, started 788)>]
    0[<_MainThread(MainThread, started 788)>, <A(Thread-1, started 7788)>]
    
    1
    2
    3
    123
    [<_MainThread(MainThread, started 788)>]'''
    

    6.线程间的通信(多线程共享全局变量)

    在一个函数中,对全局变量进行修改的时候,是否要加global要看是否对全局变量的指向进行了修改,如果修改了指向,那么必须使用global,仅仅是修改了指向的空间中的数据,此时不用必须使用global
    线程是共享全局变量

    import time
    import threading
    num = 100
    def demo1():
        global num
        num += 1
        print('demo1-nums%d'%num)
    
    def demo2():
        print('demo2-nums%d'%num)
    
    def main():
        t1 = threading.Thread(target=demo1)
        t2 = threading.Thread(target=demo2)
        t1.start()
        time.sleep(1)
        t2.start()
        print('main-nums%d'%num)
    
    if __name__ == '__main__':
        main()
    '''
    demo1-nums101
    demo2-nums101
    main-nums101
    '''
    

    7.线程间的资源竞争

    一个线程写入,一个线程读取,没问题,如果两个线程都写入呢?

    示例

    import time
    import threading
    
    num = 0
    
    
    def demo1(nums):
        global num
        for i in range(nums):
            num += 1
        print('demo1-num %d' % num)
    
    
    def demo2(nums):
        global num
        for i in range(nums):
            num += 1
        print('demo2-num %d' % num)
    
    
    def main():
        t1 = threading.Thread(target=demo1, args=(1000000,))
        t2 = threading.Thread(target=demo2, args=(1000000,))
        t1.start()
        # time.sleep(3)  # 让t1先运行3秒
        t2.start()
        time.sleep(3)
        print('main-num %d' % num)
    
    
    if __name__ == '__main__':
        main()
    '''
    demo2-num 946165
    demo1-num 1144227
    main-num 1144227'''
    
    

    解决资源竞争:在t1.start()之后暂停3秒再执行t2.start(),或者在分别在t1.start()、t2.start()之后加上t1.join(),t2.join() 或者加锁

    加锁,Lock()创建一把锁,默认是没有上锁,这样创建的锁不可重复

    示例

    import time
    import threading
    
    num = 0
    mutex = threading.Lock()  # Lock()创建一把锁,默认是没有上锁,这样创建的锁不可重复
    
    def demo1(nums):
        global num
        # 加锁
        mutex.acquire()
        for i in range(nums):
            num += 1
        # 解锁
        mutex.release()
        print('demo1-num %d' % num)
    
    
    def demo2(nums):
        global num
        # 加锁
        mutex.acquire()
        for i in range(nums):
            num += 1
        # 解锁
        mutex.release()
        print('demo2-num %d' % num)
    
    
    def main():
        t1 = threading.Thread(target=demo1, args=(1000000,))
        t2 = threading.Thread(target=demo2, args=(1000000,))
        t1.start()
        t2.start()
        time.sleep(3)
        print('main-num %d' % num)
    
    
    if __name__ == '__main__':
        main()
    '''
    demo1-num 1000000
    demo2-num 2000000
    main-num 2000000
    '''
    

    加锁,RLock()创建一把锁,默认是没有上锁,这样创建的锁可重复,要对应解锁

    示例

    import time
    import threading
    
    num = 0
    mutex = threading.RLock()  # RLock()创建一把锁,默认是没有上锁,这样创建的锁可重复,要对应解锁
    
    def demo1(nums):
        global num
        # 加锁
        mutex.acquire()
        mutex.acquire()
        for i in range(nums):
            num += 1
        # 解锁
        mutex.release()
        mutex.release()
        print('demo1-num %d' % num)
    
    
    def demo2(nums):
        global num
        # 加锁
        mutex.acquire()
        for i in range(nums):
            num += 1
        # 解锁
        mutex.release()
        print('demo2-num %d' % num)
    
    
    def main():
        t1 = threading.Thread(target=demo1, args=(1000000,))
        t2 = threading.Thread(target=demo2, args=(1000000,))
        t1.start()
        t2.start()
        time.sleep(3)
        print('main-num %d' % num)
    
    
    if __name__ == '__main__':
        main()
    
    '''
    demo1-num 1000000
    demo2-num 2000000
    main-num 2000000
    '''
    

    二、生产者和消费者模型

    1.线程间的资源竞争

    一个线程写入,一个线程读取,没问题,如果两个线程都写入呢?

    互斥锁和死锁

    互斥锁

    当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
    某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

    创建锁
    mutex = threading.Lock()
    
    锁定
    mutex.acquire()
    
    解锁
    mutex.release()
    

    死锁

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

    示例

    # 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
    import threading
    import time
    
    class MyThread1(threading.Thread):
        def run(self):
            # 对mutexA上锁
            mutexA.acquire()
    
            # mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
            print(self.name+'----do1---up----')
            time.sleep(1)
    
            # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
            mutexB.acquire()
            print(self.name+'----do1---down----')
            mutexB.release()
    
            # 对mutexA解锁
            mutexA.release()
    
    class MyThread2(threading.Thread):
        def run(self):
            # 对mutexB上锁
            mutexB.acquire()
    
            # mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
            print(self.name+'----do2---up----')
            time.sleep(1)
    
            # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
            mutexA.acquire()
            print(self.name+'----do2---down----')
            mutexA.release()
    
            # 对mutexB解锁
            mutexB.release()
    
    mutexA = threading.Lock()
    mutexB = threading.Lock()
    
    if __name__ == '__main__':
        t1 = MyThread1()
        t2 = MyThread2()
        t1.start()
        t2.start()
    '''
    执行结果:打印如下,不会结束程序
    Thread-1----do1---up----
    Thread-2----do2---up----
    '''
    
    

    避免死锁

    • 程序设计时要尽量避免
    • 添加超时时间等

    2.Queue线程

    在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么Python内置了一个线程安全的模块叫做queue模块。Python中的queue模块中提供了同步的、线程安全的队列类,包括FIFO(先进先出)队列Queue,LIFO(后入先出)队列LifoQueue。这些队列都实现了锁原语(可以理解为原子操作,即要么不做,要么都做完),能够在多线程中直接使用。可以使用队列来实现线程间的同步。

    # 初始化Queue(maxsize):创建一个先进先出的队列。
    # empty():判断队列是否为空。
    # full():判断队列是否满了。
    # get():从队列中取最后一个数据。
    # put():将一个数据放到队列中。
    from queue import Queue
    
    q = Queue()
    print(q.empty())  # True  空的队列
    print(q.full())  # False  不是满的队列
    # --------------------------分隔线--------------------------------------
    q1 = Queue()
    q1.put(1)  # 将数字1放到队列中
    print(q1.empty())  # False  不是空的队列
    print(q1.full())  # False  不是满的队列
    # --------------------------分隔线--------------------------------------
    q2 = Queue(3)  # 指定为长度为3
    q2.put(1)
    q2.put(2)
    q2.put(3)
    print(q2.empty())  # False  不是空的队列
    print(q2.full())  # True  是满的队列
    # --------------------------分隔线--------------------------------------
    q3 = Queue(3)  # 指定为长度为3
    q3.put(1)
    q3.put(2)
    q3.put(3)
    q3.put(4, timeout=3)  # 超出队列的长度,不加timeout程序将阻塞,加timeout=3,3秒后抛出异常 queue.Full
    # --------------------------分隔线--------------------------------------
    from queue import Queue
    
    q4 = Queue(3)  # 指定为长度为3
    q4.put(1)
    q4.put(2)
    q4.put(3)
    q4.put_nowait(4)  # 超出队列的长度,直接抛出异常queue.Full
    # --------------------------分隔线--------------------------------------
    q5 = Queue(3)  # 指定为长度为3
    q5.put(1)
    q5.put(2)
    q5.put(3)
    print(q5.get())  # 1
    print(q5.get())  # 2
    print(q5.get())  # 3
    print(q5.get(timeout=3))  # 超出队列,程序将阻塞,添加timeout=3,3秒后抛出异常_queue.Empty
    # --------------------------分隔线--------------------------------------
    q6 = Queue(3)  # 指定为长度为3
    q6.put(1)
    q6.put(2)
    q6.put(3)
    print(q6.get())  # 1
    print(q6.get())  # 2
    print(q6.get())  # 3
    print(q6.get_nowait())  # 超出队列的长度,直接抛出异常_queue.Empty
    

    3.生产者和消费者

    生产者和消费者模式是多线程开发中常见的一种模式。通过生产者和消费者模式,可以让代码达到高内聚低耦合的目标,线程管理更加方便,程序分工更加明确。
    生产者的线程专门用来生产一些数据,然后存放到容器中(中间变量)。消费者在从这个中间的容器中取出数据进行消费
    在这里插入图片描述

    Lock版的生产者和消费者

    import threading
    import random
    import time
    gMoney = 0
    gTimes = 0  # 定义一个变量 保存生产的次数 默认是0次
    gLock = threading.Lock()  # 定义一把锁
    
    # 定义生产者
    class Producer(threading.Thread):
    
        def run(self) -> None:
            global gMoney
            global gTimes
            # gLock.acquire()  # 上锁
            while True:
                gLock.acquire()  # 上锁
                if gTimes >= 10:
                    gLock.release()
                    break
                money = random.randint(0, 100)  # 0 <= money <= 100
                gMoney += money
                gTimes += 1
                # threading.current_thread().name 获取当前线程的名称
                print("%s生产了%d元钱,累计%d元" % (threading.current_thread().name, money, gMoney))
                gLock.release()  # 解锁
                time.sleep(1)
    # 定义消费者
    class Consumer(threading.Thread):
        def run(self) -> None:
            global gMoney
            while True:
                gLock.acquire()  # 上锁
                money = random.randint(0, 100)  # 0 <= money <= 100
                if gMoney >= money:
                    gMoney -= money
                    # threading.current_thread().name 获取当前线程的名称
                    print("%s消费了%d元钱,余额:%d元" % (threading.current_thread().name, money, gMoney))
                else:
                    if gTimes >= 10:
                        gLock.release()
                        break
                    print("%s想消费%d元钱,但是余额只有%d元,不能消费" % (threading.current_thread().name, money, gMoney))
                gLock.release()  # 解锁
                time.sleep(1)
    
    def main():
        # 开启5个生产者线程
        for i in range(5):
            th = Producer(name="生产者%d号" % i)
            th.start()
        # 开启5个消费者线程
        for i in range(5):
            th = Consumer(name="消费者%d号" % i)
            th.start()
    
    if __name__ == '__main__':
        main()
    
    '''
    生产者0号生产了13元钱,累计13元
    生产者1号生产了77元钱,累计90元
    生产者2号生产了56元钱,累计146元
    生产者3号生产了39元钱,累计185元
    生产者4号生产了10元钱,累计195元
    消费者0号消费了48元钱,余额:147元
    消费者1号消费了92元钱,余额:55元
    消费者2号想消费88元钱,但是余额只有55元,不能消费
    消费者3号想消费68元钱,但是余额只有55元,不能消费
    消费者4号消费了15元钱,余额:40元
    生产者2号生产了77元钱,累计117元
    消费者1号消费了99元钱,余额:18元
    生产者1号生产了79元钱,累计97元
    消费者0号消费了79元钱,余额:18元
    消费者4号想消费40元钱,但是余额只有18元,不能消费
    生产者0号生产了65元钱,累计83元
    生产者4号生产了29元钱,累计112元
    消费者3号消费了64元钱,余额:48元
    生产者3号生产了95元钱,累计143元
    消费者2号消费了73元钱,余额:70元
    消费者1号消费了13元钱,余额:57元
    消费者4号消费了48元钱,余额:9元
    消费者1号消费了1元钱,余额:8元
    '''
    '''
    

    Condition版的生产者和消费者

    # 相对Lock版的生产者和消费者,Lock版消耗资源更多。Condition增加了阻塞等待
    import threading
    import random
    import time
    gMoney = 0
    # 定义一个变量 保存生产的次数 默认是0次
    gTimes = 0
    # 定义一把锁
    gCond = threading.Condition()
    
    # 定义生产者
    class Producer(threading.Thread):
        def run(self) -> None:
            global gMoney
            global gTimes
            # gLock.acquire()  # 上锁
            while True:
                gCond.acquire()  # 上锁
                if gTimes >= 10:
                    gCond.release()
                    break
                money = random.randint(0, 100)  # 0 <= money <= 100
                gMoney += money
                gTimes += 1
                print("%s生产了%d元钱,累计%d元" % (threading.current_thread().name, money, gMoney))
                gCond.notifyAll()  # 通知所有
                gCond.release()  # 解锁
                time.sleep(1)
    # 定义消费者
    class Consumer(threading.Thread):
        def run(self) -> None:
            global gMoney
            while True:
                gCond.acquire()  # 上锁
                money = random.randint(0, 100)
                while gMoney < money:
                    if gTimes >= 10:
                        gCond.release()  # 解锁
                        return  # 这里如果用break退出了内循环,但是外层循环没有退出,直接用return
                    print("%s想消费%d元钱,但是余额只有%d元,不能消费" % (threading.current_thread().name, money, gMoney))
                    gCond.wait()  # 等待
                # 开始消费
                gMoney -= money
                print("%s消费了%d元钱,余额:%d元" % (threading.current_thread().name, money, gMoney))
                gCond.release()  # 解锁
                time.sleep(1)
    def main():
        # 开启5个生产者线程
        for i in range(5):
            th = Producer(name="生产者%d号" % i)
            th.start()
        # 开启5个消费者线程
        for i in range(5):
            th = Consumer(name="消费者%d号" % i)
            th.start()
    
    if __name__ == '__main__':
        main()
    
    '''
    生产者0号生产了28元钱,累计28元
    生产者1号生产了0元钱,累计28元
    生产者2号生产了94元钱,累计122元
    生产者3号生产了68元钱,累计190元
    生产者4号生产了29元钱,累计219元
    消费者0号消费了35元钱,余额:184元
    消费者1号消费了96元钱,余额:88元
    消费者2号消费了33元钱,余额:55元
    消费者3号消费了51元钱,余额:4元
    消费者4号想消费75元钱,但是余额只有4元,不能消费
    生产者1号生产了63元钱,累计67元
    生产者3号生产了57元钱,累计124元
    消费者2号消费了75元钱,余额:49元
    消费者1号想消费90元钱,但是余额只有49元,不能消费
    生产者2号生产了84元钱,累计133元
    消费者1号消费了90元钱,余额:43元
    生产者0号生产了40元钱,累计83元
    消费者0号消费了43元钱,余额:40元
    生产者4号生产了3元钱,累计43元
    消费者1号消费了4元钱,余额:39元
    消费者2号消费了19元钱,余额:20元
    消费者0号消费了18元钱,余额:2元
    '''
    

    三、多线程爬取王者荣耀高清壁纸案例

    import os
    import queue
    from urllib import parse
    from urllib.request import urlretrieve
    import requests
    import threading
    
    headers = {
        'referer':'https://pvp.qq.com/',
        'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'
    }
    
    all_name_list = []
    num = 0
    
    # 定义生产者
    class Producer(threading.Thread):
        def __init__(self, page_queue, image_queue, *args, **kwargs):
            super(Producer, self).__init__(*args, **kwargs)
            self.page_queue = page_queue
            self.image_queue = image_queue
    
        def run(self) -> None:
            while not self.page_queue.empty():
                page_url = self.page_queue.get()
                reponse = requests.get(page_url, verify=False, headers=headers).json()
                result = reponse['List']
                for data in result:
                    # 获取图片的url
                    image_urls = extract_images(data)
                    # 获取图片名字
                    name = parse.unquote(data['sProdName']).replace('1:1', '').strip()
                    # name = parse.unquote(data['sProdName']).strip() 引发下面报错
                    # FileNotFoundError: [WinError 3] 系统找不到指定的路径。: '1:1等身雕塑·铠'
                    while name in all_name_list:  # 判断是否重名
                        name = name + str(num + 1)
                    all_name_list.append(name)
                    # 创建文件夹
                    dir_path = os.path.join('image', name)  # dir_path = 'image/%s' % name 也可以
                    if not os.path.exists(dir_path):
                        os.mkdir(dir_path)
                    # 把图片的url放进队列里
                    for index, image_url in enumerate(image_urls):
                        self.image_queue.put(
                            {'image_url': image_url, 'image_path': os.path.join(dir_path, '%d.jpg' % (index + 1))})
    
    
    # 定义消费者
    class Consumer(threading.Thread):
        def __init__(self, image_queue, *args, **kwargs):
            super(Consumer, self).__init__(*args, **kwargs)
            self.image_queue = image_queue
    
        def run(self) -> None:
            failed_image_obj = []  # 空列表存放下载失败的image_url和image_path
            while True:
                try:
                    # 获取图片的url和下载路径
                    image_obj = self.image_queue.get(timeout=10)
                    image_url = image_obj.get('image_url')
                    image_path = image_obj.get('image_path')
                    # 下载图片
                    try:
                        urlretrieve(image_url, image_path)
                        print(image_path, '下载完成!')
                    except:
                        print(image_path, '下载失败!')
                        failed_image_obj.append((image_url, image_path))
                except:
                    break
            for item in failed_image_obj:  # 最后再次尝试下载失败的图片
                try:
                    urlretrieve(item[0], item[1])
                    print(item[1], '再次下载,终于成功了!')
                except:
                    print(item[0], item[1], '还是下载失败!')
    
    
    def extract_images(data):
        image_urls = []
        for i in range(1, 9):
            image_url = parse.unquote(data['sProdImgNo_%d' % i]).rstrip('200') + '0'
            # image_url = parse.unquote(data['sProdImgNo_%d' % i]).replace('200', '0')
            image_urls.append(image_url)
        return image_urls
    
    
    def main():
        # 创建页数的队列
        page_queue = queue.Queue(25)
        # 创建图片的队列
        image_queue = queue.Queue(1000)
    
        for i in range(25):
            url = 'https://apps.game.qq.com/cgi-bin/ams/module/ishow/V1.0/query/workList_inc.cgi?activityId=2735&sVerifyCode=ABCD&sDataType=JSON&iListNum=20&totalpage=0&page={}&iOrder=0&iSortNumClose=1&&_everyRead=true&iTypeId=2&iFlowId=267733&iActId=2735'.format(
                i)
            # 把页数url添加到页数的队列中
            page_queue.put(url)
        # 定义3个生产者线程
        for i in range(3):
            p = Producer(page_queue, image_queue)
            p.start()
    
        # 定义8个消费者线程
        for i in range(8):
            c = Consumer(image_queue)
            c.start()
    
    
    if __name__ == '__main__':
        main()
    
    

    得到全部数据:
    在这里插入图片描述

    展开全文
  • 多线程设计模式之保护性暂停模式

    万次阅读 2020-09-14 14:56:30
    多线程设计模式之保护性暂停模式 定义 保护性暂停模式(Guarded Suspension Design Pattern):当线程在访问某个对象时,发现条件不满足时,就暂时挂起等待条件满足时再次访问。 如果某个结果需要多线程之间传递,...
  • 511遇见易语言多线程大漠多线程-1进程线程多线程511遇见易语言多线程大漠多线程-2中转子程序传多参511遇见易语言多线程大漠多线程-3线程传参数据变量地址511遇见易语言多线程大漠多线程-4线程传参指针地址511遇见...
  • Java 多线程编程基础(详细)

    万次阅读 多人点赞 2020-11-03 17:36:30
    Java多线程编程基础进程与线程多线程实现Thread类实现多线程Runnable接口实现多线程Callable接口实现多线程多线程运行状态多线程常用操作方法线程的命名和获取线程休眠线程中断线程强制执行线程让步线程优先级设定...
  • 部分主要阐述 Thread 的基础知识,详细介绍线程的 API 使用、线程安全、线程间数据通信,以及如何保护共享资源等内容,它是深入学习多线程内容的基础。 第二部分引入了 ClassLoader,这是因为 ClassLoader 与线程...
  • JavaFX 中使用多线程与保证 UI 线程安全JavaFX 中使用多线程JavaFX 中保证 UI 线程安全总结与补充   JavaFX 中的 UI 和大多数其它的编程语言一样,是单线程的。前人很早就已经多次尝试在 UI 上使用多线程,大多都...
  • 多线程的实现和使用场景

    万次阅读 多人点赞 2021-06-09 22:06:20
    多线程的实现和使用场景一、多线程实现方式1.1 Thread实现1.2 Runnable实现二、多线程的使用场景1.多线程使用场景1.1 多线程应该最多的场景:1.2多线程的常见应用场景:2.多线程小案列2.1 多线程计算2.2 多线程实现...
  • 多线程,到底该设置多少个线程?

    千次阅读 2019-06-02 13:26:30
    作者:享学课堂老顾 微信公众号: 享学课堂online ...内含SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!
  • c 多线程编程01

    千次阅读 2018-10-21 00:53:57
    Java 老师希望我们尝试进行 Java 的多线程编程,也希望我们能够去实现一下 C 语言的多线程编程。用以体会不同编程语言间的多线程编程。借此机会,初步学习一下 C 语言的多线程编程。 第一部分主要内容如下: ...
  • Linux: 多线程

    千次阅读 2021-11-29 10:42:49
    线程概念: 线程是进程中的一条执行流程. 在linux之前学习进程的时候 ,进程就是一个pcb, 但是在现在学习线程的时候, 发现线程是进程中的一条执行流,而因为linux下执行流是通过pcb来完成的,所以理解pcb是linux...
  • 《COM本质论》 《COM+技术内幕》 511遇见易语言多线程大漠多线程 二、认识大漠插件 Dm.dll插件目前是用COM架构设计的,属于第三方COM对象,要想在我们自己的程序里引用(同进程内调用),需要注册,或免注册直接调用...
  • 线程、进程、多线程、多进程 和 多任务 小结

    千次阅读 多人点赞 2019-04-20 11:59:56
    4 多线程 5 线程与进程的关系 6 线程和进程的区别 7 进程的优缺点 7.1 进程的优点 7.2 进程的缺点 8 线程的优缺点 8.1 线程的优点 8.2 线程的缺点 9 多线程的优缺点 9.1 多线程的优点 9.2 多线程的缺点 ...
  • 是一套游戏循环账号任务的多线程模板,和上一套的区别是,上一套大漠自动切换账号模板是单线程,是一个账号完成任务自动退出,自动切换登录另一个账号,而本套是同时开启多个账号窗口,提高了效率,适合账号非常多...
  • Java多线程学习(吐血超详细总结)

    万次阅读 多人点赞 2015-03-14 13:13:17
    本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。
  • Redis是目前广为人知的一个内存数据库,在各个场景中都有着非常丰富的应用,前段时间Redis推出了6.0的版本,在新版本中采用了多线程模型。 因为我们公司使用的内存数据库是自研的,按理说我对Redis的关注其实并不算...
  • 系统接受实现多用户多请求的高并发时,通过多线程来实现。 二、线程后台处理大任务 一个程序是线性执行的。如果程序执行到要花大量时间处理的任务时,那主程序就得等待其执行完才能继续执行下面的。那用户就不得...
  • 多线程(线程概念、代码示例)

    千次阅读 2019-06-02 00:11:47
    在一个进程中包含若干个线程线程是独立的执行路径,一个进程中至少含有一个线程,像我们已知的主线程和JC线程(垃圾回收),即使我们没有自己创建线程,后台也会自动存在上面提到的两个和其他一些线程; 我们运行...
  • Python多线程-手慢无的真相

    千次阅读 多人点赞 2021-02-24 15:44:46
    我们常说的「手慢无」其实类似多线程同时竞争一个共享资源的结果,要保证结果的唯一正确性,而这让我们从线程(Python)慢慢说起…… 文章目录 线程的概念 创建多线程 主线程 阻塞线程 线程方法 线程同步 同步的概念...
  • Java多线程面试题(面试必备)

    万次阅读 多人点赞 2020-05-26 01:15:38
    文章目录一、多线程基础基础知识1. 并发编程1.1 并发编程的优缺点1.2 并发编程的三要素1.3 并发和并行有和区别1.4 什么是多线程多线程的优劣?2. 线程与进程2.1 什么是线程与进程2.2 线程与进程的区别2.3 用户线程...
  • QT多线程编程详解

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

    万次阅读 多人点赞 2019-01-01 16:20:56
    文章目录前言进程与线程继承Thread类,实现多线程FAQ 为什么多线程的启动不直接使用run()方法而必须使用Thread类中start()方法呢?基于Runnable接口实现多线程Thread 与 Runnable 的关系Callable实现多线程线程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 838,612
精华内容 335,444
关键字:

做设计需要多线程吗