精华内容
下载资源
问答
  • 多线程的时间上限
    千次阅读 多人点赞
    2020-04-19 11:28:07

     

    多进程和多线程的主要区别是:线程是进程的子集(部分),一个进程可能由多个线程组成。多进程的数据是分开的、共享复杂,需要用IPC;但同步简单。多线程共享进程数据,共享简单;但同步复杂。

      多进程,Windows 应用程序中消息有两种送出途径;直接和排队。Windows或某些运行的应用程序可直接发布消息给窗口过程,或者,消息可送到消息列象连续不断轮询消息队列的OS中当前执行的每个进程都 事件驱动程序不是由事件的顺序来控制,而是由事件的发生来控,而事件的发生是随机的、不确定的,这就允许程序的用户用各种合理的顺序来安排程序的流程。

      多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。[1] 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。

    一、多进程和多线程的区别是什么?

      在Linux下编程多用多进程编程少用多线程编程。

      IBM有个家伙做了个测试,发现切换线程context的时候,windows比linux快一倍多。进出最快的锁(windows2k的 critical section和linux的pthread_mutex),windows比linux的要快五倍左右。当然这并不是说linux不好,而且在经过实际编程之后,综合来看我觉得linux更适合做high performance server,不过在多线程这个具体的领域内,linux还是稍逊windows一点。这应该是情有可原的,毕竟unix家族都是从多进程过来的,而 windows从头就是多线程的。

      如果是UNIX/linux环境,采用多线程没必要。

      多线程比多进程性能高?误导!

      应该说,多线程比多进程成本低,但性能更低。

      在UNIX环境,多进程调度开销比多线程调度开销,没有显著区别,就是说,UNIX进程调度效率是很高的。内存消耗方面,二者只差全局数据区,现在内存都很便宜,服务器内存动辄若干G,根本不是问题。

      多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不堵车。

      多线程是平面交通系统,造价低,但红绿灯太多,老堵车。

      我们现在都开跑车,油(主频)有的是,不怕上坡下坡,就怕堵车。

      高性能交易服务器中间件,如TUXEDO,都是主张多进程的。实际测试表明,TUXEDO性能和并发效率是非常高的。TUXEDO是贝尔实验室的,与UNIX同宗,应该是对UNIX理解最为深刻的,他们的意见应该具有很大的参考意义。

      二、多进程和多线程的优缺点分析:

      多进程:

      多进程优点:

      1、每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;

      2、通过增加CPU,就可以容易扩充性能;

      3、可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;

      4、每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大。

      多进程缺点:

      1、逻辑控制复杂,需要和主程序交互;

      2、需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大;

      3、最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。当然你也可以利用多线程+多CPU+轮询方式来解决问题……

      4、方法和手段是多样的,关键是自己看起来实现方便有能够满足要求,代价也合适。

      多线程:

      多线程的优点:

      1、无需跨进程边界;

      2、程序逻辑和控制方式简单;

      3、所有线程可以直接共享内存和变量等;

      4、线程方式消耗的总资源比进程方式好。

      多线程缺点:

      1、每个线程与主程序共用地址空间,受限于2GB地址空间;

      2、线程之间的同步和加锁控制比较麻烦;

      3、一个线程的崩溃可能影响到整个程序的稳定性;

      4、到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;

      5、线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU。

    参考:

    多进程和多线程的区别是什么​? 

    更多相关内容
  • 本文实例讲述了Python实现可设置持续运行时间、线程数及时间间隔的多线程异步post请求功能。分享给大家供大家参考,具体如下: #coding=utf8 ''' random.randint(a, b):用于生成一个指定范围内的整数。 其中参数a是...
  • 多线程,到底该设置多少个线程?

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

    作者:享学课堂老顾

    微信公众号: 享学课堂online

    在这里插入图片描述

    一、前言

    “不好了,线上服务器超时严重,请求非常慢,好像报连接数too many了,怎么办?“小伙伴们在反馈。一般我们的技术老大的处理方式,把连接数和线程池调大点,重启,再观察。

    往往这个方式是应急措施,治标不治本,因为不知道问题的原因。

    有个严重误区,以为线程池设置太小了,调大点请求就会快了。

    今天就带着小伙伴们沟通一下,线程池的大小应该如何合理的设置其大小?

    二、问题

    如果有两个任务需要处理,一个任务A,一个任务B

    方案一:一个线程执行任务A和B,A执行完后,执行B
    方案二:两个线程A和B去执行任务A 和 B,同时进行

    哪个方案会快点?应该很多人会回答,肯定是方案二啊,多线程并行去处理任务A和B,肯定快啊。是这样吗?回答这个问题之前,先带着大家去回顾梳理一下。

    三、线程执行

    线程的执行,是由CPU进行调度的,一个CPU在同一时刻只会执行一个线程,我们看上去的线程A 和 线程B并发执行。

    为了让用户感觉这些任务正在同时进行,操作系统利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。

    上下文切换过程是需要时间的;现在我们来看一下上面的问题,小伙伴们再看一下是哪个方案快呢?是不是有些小伙伴们会说方案一,因为不需要线程切换;方案二需要来回切换这两个线程,耗时会多点。

    小伙伴们心中此时是不是会有疑惑,那为什么会有多线程?先不急,再往下看。

    四、为什么要多线程

    小伙伴想想在我们真实业务中,我们是什么流程?

    在这里插入图片描述

    上图的流程:

    1、先发起网络请求

    2、Web服务器解析请求

    3、请求后端的数据库获取数据

    4、获取数据后,进行处理

    5、把处理结果放回给用户

    这个是我们处理业务的时候,常规的请求流程;我们看一下整个过程涉及到什么计算机处理。

    1、网络请求----->网络IO

    2、解析请求----->CPU

    3、请求数据库----->网络IO

    4、MySQL查询数据----->磁盘IO

    5、MySQL返回数据----->网络IO

    6、数据处理----->CPU

    7、返回数据给用户----->网络IO

    讲到这里,小伙伴们是不是感觉又不乱了,在真实业务中我们不单单会涉及CPU计算,还有网络IO和磁盘IO处理,这些处理是非常耗时的。如果一个线程整个流程是上图的流程,真正涉及到CPU的只有2个节点,其他的节点都是IO处理,那么线程在做IO处理的时候,CPU就空闲出来了,CPU的利用率就不高。

    小伙伴们现在知道多线程的用处了吧,对,就是为了提升CPU利用率。

    五、提升QPS/TPS

    衡量系统性能如何,主要指标系统的(QPS/TPS)

    QPS/TPS:每秒能够处理请求/事务的数量

    并发数:系统同时处理的请求/事务的数量

    响应时间:就是平均处理一个请求/事务需要时长

    QPS/TPS = 并发数/响应时间

    上面公式代表并发数越大,QPS就越大;所以很多人就会以为调大线程池,并发数就会大,也会提升QPS,所以才会出现一开始前言所说的,大多数人的误区。

    其实QPS还跟响应时间成反比,响应时间越大,QPS就会越小。

    虽然并发数调大了,就会提升QPS,但线程数也会影响响应时间,因为上面我们也提到了上下文切换的问题,那怎么设置线程数的呢?

    六、如何设置线程数

    那我们如何分配线程?我们提供一个公式:

    最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

    备注:这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。

    我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。

    从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升CPU利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。

    七、基础常规标准

    那我们小伙伴们会问,因为很多业务集中到一个线程池中,不像上面的案例比较简单,事实上业务太多,怎么设置呢?这个就是要去压力测试去调整。不过我们的前辈已经帮我们总结了一个基础的值(最终还是要看运行情况自行调整)

    1、CPU密集型:操作内存处理的业务,一般线程数设置为:CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 8

    2、IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40

    八、总结

    今天介绍了线程数大小的设置,一些小伙伴们的误区。讲到这里我们小伙伴们是不是对线程有了更新的理解,不像之前那么粗暴,应该要去分析为什么这么慢,系统的瓶颈出现在什么地方,减少瓶颈的耗时。

    另外,推荐小伙伴们再去看一下Redis、Nginx;为什么他们会那么快呢?其实和这篇文章的知识点有共同的地方。

    热门内容:

    1、系统又出Bug?这几个调优攻略,帮你解决大部分问题!

    2、生产环境下到底该如何部署Tomcat?

    3、神级代码注释鉴赏,喜欢就拿去用!

    4、开源的13个Spring Boot 优秀学习项目!超53K星,一网打尽!

    5、华为海思总裁致员工信:不再有换胎机会,必当科技自立!

    6、不了解这12个语法糖,别说你会Java!

    7、小小的分页问题引发的加班血案!

    8、1万属性,100亿数据,每秒10万吞吐,架构如何设计?

    在这里插入图片描述

    【视频福利】2T免费学习视频,搜索或扫描上述二维码关注微信公众号:Java后端技术(ID: JavaITWork)回复:1024,即可免费获取!内含SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!

    展开全文
  • 【C++多线程】限制线程数量

    千次阅读 2021-03-13 12:12:01
    线程数量并不是越越好,有些情况下,需要限制线程的数量。 这里需要用到互斥锁mutex,条件变量condition_variable,通用互斥锁包装器unique_lock std::unique_lock也可以提供自动加锁、解锁功能,比std::lock_...

    线程数量并不是越多越好,有些情况下,需要限制线程的数量。

    这里需要用到互斥锁mutex,条件变量condition_variable,通用互斥锁包装器unique_lock

    std::unique_lock也可以提供自动加锁、解锁功能,比std::lock_guard更加灵活。

    unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。

    unique_locklock_guard使用更加灵活,功能更加强大。

    使用unique_lock需要付出更多的时间、性能成本。

    https://www.cnblogs.com/xudong-bupt/p/9194394.html 

    全局的互斥锁m,用于锁定“process_number”,搭配unique_lock使用。

     

     

    当一个线程执行完了之后就让process_number减1,然后唤醒所有等待线程

    条件变量(condition variable),c++11中提供了#include <condition_variable>头文件,其中的std::condition_variable可以和std::mutex结合一起使用,其中有两个重要的接口,notify_one()和wait(),

    wait()可以让当前线程陷入休眠状态。(也就是程序停止在wait处不再往下执行)

    notify_all()就是唤醒处于wait中的所有线程(可能当时有很多线程都处于wait状态)。
    ————————————————
    版权声明:本文为CSDN博主「yxpandjay」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/yxpandjay/article/details/109302863

     

    这里再condition.wait(lck)外面还要套一层循环while(process_number>5)的意思就是,因为同时唤醒了很多线程,

    while (process_number > 5) {
        condition.wait(lck);
    }

    如果没有这一层循环可能会有两个或者多个线程反应快同时启动,那就不可能限制住线程数量了,

    因此需要在每个线程重启启动的时候,都去看看循环中的条件是否满足,再去执行,如果有其他线程抢先了,就继续wait

     

    完整代码:

    #include <iostream>  
    #include <thread>
    #include <mutex>
    #include <stdio.h>
    #include<vector>
    using namespace std;
    mutex m;
    condition_variable condition;
    int process_number;
    
    void t1(int id) {
    	if (process_number > 5) {
    		unique_lock<mutex> lck(m);
    		printf("id: %d, process_number: %d, waiting thread ID %d\n", id, process_number, std::this_thread::get_id());
    		while (process_number > 5) {
    			condition.wait(lck);
    		}
    	}
    	process_number += 1;
    
    	printf("id: %d, process_number: %d, doing thread ID %d\n", id, process_number, std::this_thread::get_id());
    	double sum = 0;
    	for (double i = 0; i < 1e10; i++) {
    		sum += i;
    	}
    	printf("id: %d, process_number: %d, doing thread ID %d get sum = %f\n", id, process_number, std::this_thread::get_id(),sum);
    
    	process_number -= 1;
    	condition.notify_all();
    }
    
    int main() {
    	process_number = 0;
    	vector<thread> th_set;
    	for (int i = 0; i < 20; i++) {
    		//thread th1(t1,i);
    		//th1.join();
    		th_set.push_back(thread(t1, i));
    	}
    	for (auto &th : th_set) {
    		th.join();
    	}
    	system("pause");
    	return 0;
    }

    一开始有6个线程开始工作,其他线程等待。

    当id=3的线程执行结束了之后,id=9的线程开始执行。

     

     

    这里要注意的是,再for循环中开启线程不可以这样写:

    for (int i = 0; i < 20; i++) {
        thread th1(t1,i);
        th1.join();
    }

    因为join是子线程执行完后主线程回收子线程资源,本来没啥毛病,但是用在for循环中就会导致主线程在for循环里面等着线程执行完了回收资源,那就等于顺序执行,同一时间只执行一个线程(自行测试)

    那么解决方法就是先用一个vector把线程先装起来,等for循环结束以后再统一回收资源。

    vector<thread> th_set;
    for(int i = 0; i < 20; i++) {
        th_set.push_back(thread(t1, i));
    }

     

     

    展开全文
  • 【python】多线程详解

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

    一、进程与线程关系

    一个进程至少包含一个线程。

    二、线程基础

    1、线程的状态

    线程有5种状态,状态转换的过程如下图所示:

    7a461d48d65903961f38caf591785849.png

    2、线程同步(锁)

    多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。考虑这样一种情况:一个列表里所有元素都是0,线程”set”从后向前把所有元素改成1,而线程”print”负责从前往后读取列表并打印。那么,可能线程”set”开始改的时候,线程”print”便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

    锁有两种状态——锁定和未锁定。每当一个线程比如”set”要访问共享数据时,必须先获得锁定;如果已经有别的线程比如”print”获得锁定了,那么就让线程”set”暂停,也就是同步阻塞;等到线程”print”访问完毕,释放锁以后,再让线程”set”继续。经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

    线程与锁的交互如下图所示:

    3ef442a790361a6c9fc539dfd07c0506.png

    3、线程通信(条件变量)

    然而还有另外一种尴尬的情况:列表并不是一开始就有的;而是通过线程”create”创建的。如果”set”或者”print” 在”create”还没有运行的时候就访问列表,将会出现一个异常。使用锁可以解决这个问题,但是”set”和”print”将需要一个无限循环——他们不知道”create”什么时候会运行,让”create”在运行后通知”set”和”print”显然是一个更好的解决方案。于是,引入了条件变量。

    条件变量允许线程比如”set”和”print”在条件不满足的时候(列表为None时)等待,等到条件满足的时候(列表已经创建)发出一个通知,告诉”set” 和”print”条件已经有了,你们该起床干活了;然后”set”和”print”才继续运行。

    线程与条件变量的交互如下图所示:

    8b35120e2430ff2d081dfd09c521b404.png

    4、线程运行和阻塞的状态转换

    最后看看线程运行和阻塞状态的转换

    2fa80e5a067356d42517edd5b90b942e.png

    阻塞有三种情况:

    同步阻塞(锁定池)是指处于竞争锁定的状态,线程请求锁定时将进入这个状态,一旦成功获得锁定又恢复到运行状态;

    等待阻塞(等待池)是指等待其他线程通知的状态,线程获得条件锁定后,调用“等待”将进入这个状态,一旦其他线程发出通知,线程将进入同步阻塞状态,再次竞争条件锁定;

    而其他阻塞是指调用time.sleep()、anotherthread.join()或等待IO时的阻塞,这个状态下线程不会释放已获得的锁定。

    四、threading

    threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是对象的基本行为(每一个对象都自带了锁和条件变量),而在Python中则是独立的对象。Python Thread提供了Java Thread的行为的子集;没有优先级、线程组,线程也不能被停止、暂停、恢复、中断。Java Thread中的部分被Python实现了的静态方法在threading中以模块方法的形式提供。

    threading 模块提供的常用方法:

    threading.currentThread(): 返回当前的线程变量。

    threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

    threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    threading模块提供的类:

    Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local.

    1、Thread

    Thread是线程类,与Java类似,有两种使用方法,直接传入要运行的方法或从Thread继承并覆盖run():

    # encoding: UTF-8

    import threading

    # 方法1:将要执行的方法作为参数传给Thread的构造方法

    def func():

    print 'func() passed to Thread'

    t = threading.Thread(target=func)

    t.start()

    # 方法2:从Thread继承,并重写run()

    class MyThread(threading.Thread):

    def run(self):

    print 'MyThread extended from Thread'

    t = MyThread()

    t.start()

    构造方法:

    Thread(group=None, target=None, name=None, args=(), kwargs={})

    group: 线程组,目前还没有实现,库引用中提示必须是None;

    target: 要执行的方法;

    name: 线程名;

    args/kwargs: 要传入方法的参数。

    实例方法:

    isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。

    get/setName(name): 获取/设置线程名。

    is/setDaemon(bool): 获取/设置是否守护线程。初始值从创建该线程的线程继承。当没有非守护线程仍在运行时,程序将终止。

    start(): 启动线程。

    join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。

    一个使用join()的例子:

    # encoding: UTF-8

    import threading

    import time

    def context(tJoin):

    print 'in threadContext.'

    tJoin.start()

    # 将阻塞tContext直到threadJoin终止。

    tJoin.join()

    # tJoin终止后继续执行。

    print 'out threadContext.'

    def join():

    print 'in threadJoin.'

    time.sleep(1)

    print 'out threadJoin.'

    # tJoin和tContext分别为两个不同的线程

    tJoin = threading.Thread(target=join)

    tContext = threading.Thread(target=context, args=(tJoin,))

    tContext.start()

    运行结果:

    in threadContext.

    in threadJoin.

    out threadJoin.

    out threadContext

    2、Lock

    Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

    可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。

    构造方法:

    Lock()

    实例方法:

    acquire([timeout]): 使线程进入同步阻塞状态,尝试获得锁定。

    release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。

    # encoding: UTF-8

    import threading

    import time

    data = 0

    lock = threading.Lock()

    def func():

    global data

    print '%s acquire lock...' % threading.currentThread().getName()

    # 调用acquire([timeout])时,线程将一直阻塞,

    # 直到获得锁定或者直到timeout秒后(timeout参数可选)。

    # 返回是否获得锁。

    if lock.acquire():

    print '%s get the lock.' % threading.currentThread().getName()

    data += 1

    time.sleep(2)

    print '%s release lock...' % threading.currentThread().getName()

    # 调用release()将释放锁。

    lock.release()

    t1 = threading.Thread(target=func)

    t2 = threading.Thread(target=func)

    t3 = threading.Thread(target=func)

    t1.start()

    t2.start()

    t3.start()

    多运行几次,你会看到打印的信息顺序并不一致,这就证实了线程在锁定池中谁将获得锁运行是由系统调度决定(随机,不确定)

    RLock

    RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

    可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

    构造方法:

    RLock()

    实例方法:

    acquire([timeout])/release(): 跟Lock差不多。

    # encoding: UTF-8

    import threading

    import time

    rlock = threading.RLock()

    def func():

    # 第一次请求锁定

    print '%s acquire lock...' % threading.currentThread().getName()

    if rlock.acquire():

    print '%s get the lock.' % threading.currentThread().getName()

    time.sleep(2)

    # 第二次请求锁定

    print '%s acquire lock again...' % threading.currentThread().getName()

    if rlock.acquire():

    print '%s get the lock.' % threading.currentThread().getName()

    time.sleep(2)

    # 第一次释放锁

    print '%s release lock...' % threading.currentThread().getName()

    rlock.release()

    time.sleep(2)

    # 第二次释放锁

    print '%s release lock...' % threading.currentThread().getName()

    rlock.release()

    t1 = threading.Thread(target=func)

    t2 = threading.Thread(target=func)

    t3 = threading.Thread(target=func)

    t1.start()

    t2.start()

    t3.start()

    4、Condition

    Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

    可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。

    构造方法:

    Condition([lock/rlock])

    实例方法:

    acquire([timeout])/release(): 调用关联的锁的相应方法。

    wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。

    notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

    notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

    例子是很常见的生产者/消费者模式:

    # encoding: UTF-8

    import threading

    import time

    # 商品

    product = None

    # 条件变量

    con = threading.Condition()

    # 生产者方法

    def produce():

    global product

    if con.acquire():

    while True:

    if product is None:

    print 'produce...'

    product = 'anything'

    # 通知消费者,商品已经生产

    con.notify()

    # 等待通知

    con.wait()

    time.sleep(2)

    # 消费者方法

    def consume():

    global product

    if con.acquire():

    while True:

    if product is not None:

    print 'consume...'

    product = None

    # 通知生产者,商品已经没了

    con.notify()

    # 等待通知

    con.wait()

    time.sleep(2)

    t1 = threading.Thread(target=produce)

    t2 = threading.Thread(target=consume)

    t2.start()

    t1.start()

    5、Semaphore/BoundedSemaphore

    Semaphore(信号量)是计算机科学史上最古老的同步指令之一。Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release() 时+1。计数器不能小于0;当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。

    基于这个特点,Semaphore经常用来同步一些有“访客上限”的对象,比如连接池。

    BoundedSemaphore 与Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

    构造方法:

    Semaphore(value=1): value是计数器的初始值。

    实例方法:

    acquire([timeout]): 请求Semaphore。如果计数器为0,将阻塞线程至同步阻塞状态;否则将计数器-1并立即返回。

    release(): 释放Semaphore,将计数器+1,如果使用BoundedSemaphore,还将进行释放次数检查。release()方法不检查线程是否已获得 Semaphore。

    # encoding: UTF-8

    import threading

    import time

    # 计数器初值为2

    semaphore = threading.Semaphore(2)

    def func():

    # 请求Semaphore,成功后计数器-1;计数器为0时阻塞

    print '%s acquire semaphore...' % threading.currentThread().getName()

    if semaphore.acquire():

    print '%s get semaphore' % threading.currentThread().getName()

    time.sleep(4)

    # 释放Semaphore,计数器+1

    print '%s release semaphore' % threading.currentThread().getName()

    semaphore.release()

    t1 = threading.Thread(target=func)

    t2 = threading.Thread(target=func)

    t3 = threading.Thread(target=func)

    t4 = threading.Thread(target=func)

    t1.start()

    t2.start()

    t3.start()

    t4.start()

    time.sleep(2)

    # 没有获得semaphore的主线程也可以调用release

    # 若使用BoundedSemaphore,t4释放semaphore时将抛出异常

    print 'MainThread release semaphore without acquire'

    semaphore.release()

    6、Event

    Event(事件)是最简单的线程通信机制之一:一个线程通知事件,其他线程等待事件。Event内置了一个初始为False的标志,当调用set()时设为True,调用clear()时重置为 False。wait()将阻塞线程至等待阻塞状态。

    Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

    构造方法:

    Event()

    实例方法:

    isSet(): 当内置标志为True时返回True。

    set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。

    clear(): 将标志设为False。

    wait([timeout]): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

    # encoding: UTF-8

    import threading

    import time

    event = threading.Event()

    def func():

    # 等待事件,进入等待阻塞状态

    print '%s wait for event...' % threading.currentThread().getName()

    event.wait()

    # 收到事件后进入运行状态

    print '%s recv event.' % threading.currentThread().getName()

    t1 = threading.Thread(target=func)

    t2 = threading.Thread(target=func)

    t1.start()

    t2.start()

    time.sleep(2)

    # 发送事件通知

    print 'MainThread set event.'

    event.set()

    Timer

    Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。

    构造方法:

    Timer(interval, function, args=[], kwargs={})

    interval: 指定的时间

    function: 要执行的方法

    args/kwargs: 方法的参数

    实例方法:

    Timer从Thread派生,没有增加实例方法

    # encoding: UTF-8

    import threading

    def func():

    print 'hello timer!'

    timer = threading.Timer(5, func)

    timer.start()

    8、local

    local是一个小写字母开头的类,用于管理 thread-local(线程局部的)数据。对于同一个local,线程无法访问其他线程设置的属性;线程设置的属性不会被其他线程设置的同名属性替换。

    可以把local看成是一个“线程-属性字典”的字典,local封装了从自身使用线程作为 key检索对应的属性字典、再使用属性名作为key检索属性值的细节。

    # encoding: UTF-8

    import threading

    local = threading.local()

    local.tname = 'main'

    def func():

    local.tname = 'notmain'

    print local.tname

    t1 = threading.Thread(target=func)

    t1.start()

    t1.join()

    print local.tname

    熟练掌握Thread、Lock、Condition就可以应对绝大多数需要使用线程的场合,某些情况下local也是非常有用的东西。本文的最后使用这几个类展示线程基础中提到的场景:

    # encoding: UTF-8

    import threading

    alist = None

    condition = threading.Condition()

    def doSet():

    if condition.acquire():

    while alist is None:

    condition.wait()

    for i in range(len(alist))[::-1]:

    alist[i] = 1

    condition.release()

    def doPrint():

    if condition.acquire():

    while alist is None:

    condition.wait()

    for i in alist:

    print i,

    print

    condition.release()

    def doCreate():

    global alist

    if condition.acquire():

    if alist is None:

    alist = [0 for i in range(10)]

    condition.notifyAll()

    condition.release()

    tset = threading.Thread(target=doSet,name='tset')

    tprint = threading.Thread(target=doPrint,name='tprint')

    tcreate = threading.Thread(target=doCreate,name='tcreate')

    tset.start()

    tprint.start()

    tcreate.start()

    展开全文
  • 异步执行函数线程Dome 调用MSVC CRT的函数_beginthread()或_beginthreadex()来创建线程。 _beginthread 参数和返回值 unsigned long _beginthread( void(_cdecl *start_address)(void *), //声明为void (*start_...
  • Python多线程-手慢无的真相

    千次阅读 多人点赞 2021-02-24 15:44:46
    我们常说的「手慢无」其实类似多线程同时竞争一个共享资源的结果,要保证结果的唯一正确性,而这让我们从线程(Python)慢慢说起……
  • Python多线程——队列(Queue)

    千次阅读 2019-11-10 20:37:38
    Queue主要就是为多线程生产值、消费者之间线程通信提供服务,具有先进先出的数据结构。 首先我们组要明白为什么要使用队列,队列的性质, 多线程并发编程的重点,是线程之间共享数据的访问问题和线程之间的通信问题 ...
  • 多线程(看这篇就够了!最全)

    千次阅读 2021-05-11 15:28:20
    多线程呢就是指一个进程运行时,多个线程并行交替执行的过程。可以提高代码的执行效离 解决多线程安全 1.使用synchronized synchronized的三种应用方式 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前...
  • 显示:通过向动态库的事件指针上添加回调函数,并在回调函数内将接收到的数据包存储至Queue,并在后台线程中循环取出队列中已有的数据包,并将其中的数据(电流、时间戳等)添加至已绑定LiveCharts对应的折线图序列...
  • 春招 all in C++ ...以下是我在八股八股八股的过程中, 准备的多线程的优点 提高 CPU 的利用率 线程的创建开销远比进程要小很多, 所以切换时保存上下文的速度会快很多 同时多线程的缺点 稳定性低, 不可靠, .
  • 多线程(单例模式)

    千次阅读 2019-10-29 08:58:51
    1,多线程,把耗时操作方法另外一个线程单独执行 2,使用消息列对分隔两个操作 多线程: 进程:计算机上可以单独执行的程序。 线程:执行代码的地方。 一个进程至少包含一个线程,这个线程叫做主线程。 代码在...
  • 线程、进程、多线程、多进程 和 多任务 小结

    千次阅读 多人点赞 2019-04-20 11:59:56
    4 多线程 5 线程与进程的关系 6 线程和进程的区别 7 进程的优缺点 7.1 进程的优点 7.2 进程的缺点 8 线程的优缺点 8.1 线程的优点 8.2 线程的缺点 9 多线程的优缺点 9.1 多线程的优点 9.2 多线程的缺点 ...
  • 深入浅出比喻说明线程与进程,附小白都能看懂的对比试验。
  • 1、线程池2、协程2.1代理池、协程分页爬取图片 1、线程池 模拟阻塞 import time #导入线程池模块对应的类 from multiprocessing.dummy import Pool #使用线程池方式执行 start_time = time.time() def get_page...
  • 多线程与多进程详细

    千次阅读 2021-02-04 11:05:25
    进程和线程的概念 1.进程(最小的资源单位):  进程:就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。  程序:我们编写的程序用来描述进程要完成哪些功能以及如何...
  • [并发概念] 多线程与高并发

    千次阅读 2019-12-31 08:02:47
    多线程的理解 什么是多线程 如果一个程序允许两个或者两个以上的线程,那么它便是多线程程序。多线程是指单个进程内允许多个线程。 任务分为资源独立任务和资源共享任务。独立任务不需要使用多线程;共享任务,会...
  • 要说多线程 我们明确几个概念 1.1 名词 1.1.1 操作系统 操作系统是计算机系统中的一个系统软件,是一些程序模块的集合 它们能以尽量有效、合理的方式组合和管理计算机的软硬资源 系统效率,资源利用率 CPU利用率...
  • 1、多线程工具TreadTestHttppackage com;import java.util.concurrent.CountDownLatch;public class TreadTestHttp {public static void main(String[] args) throws InterruptedException {Runnable taskTemp = new...
  • 多线程基本概念多线程有什么用?线程、进程、协程的区别什么是多线程上下文切换?什么是线程安全2. java多线程创建创建线程的方式Runnable和Callable的区别Thread类中的start()和run()方法有什么区别?什么导致线程...
  • 所以除非业务调用时的RT时间无法忍受,否则真的不建议在服务方法里起多线程。除了带来代码维护的不确定性外,从机器整体cpu能支撑的qps来看其实并没有多大提升。 有位P8表示他以前在一个部门待过,那里尽喜欢在代码...
  • 其中参数a是下限,参数b是上限,生成的随机数n: a random.choice(sequence):从序列中获取一个随机元素 参数sequence表示一个有序类型(列表,元组,字符串) ''' import httplib,json import time impo
  • 但是线程池的线程数量上线是多少呢,有如何合理估算线程数量保证性能呢,如果脑子里是问号,请往下看。 总得来说,线程池的大小(上线和理想数量)由程序所处的系统和要执行的任务资源性质和程序要执行的任务类型来...
  • 1、进程模型的优缺点 (1)优点: 1)每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系; 2)通过增加CPU,就可以容易扩充性能; 3)可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的...
  • JAVA多线程详解(超详细)

    千次阅读 多人点赞 2020-11-08 00:13:31
    线程概述1.1 什么是进程1.2 什么是线程1.3 进程和线程的区别1.4 线程的组成1.5 线程的特点2. 线程的创建2.1 创建线程(一)2.2 获取和修改线程名称2.3 一个线程小案例2.4 创建线程(二)2.5 Runnable小案例3. 线程...
  • springboot定时任务开启多线程

    千次阅读 2020-04-07 14:56:27
    在开发过程中避免不了要使用定时任务,例如:定时统计数据,上传数据等等。而springboot的定时任务Scheduled默认是单线程。所以当一个类中需要执行的定时方法过多是,任务就会排队,不...这时候就需要配置多线程了。...
  • 多线程对数据库中的库存进行修改扣减的时候,会出现超卖或是死锁情况,如下: 问题一:超卖 当多个线程执行select然后再进行update时,由于mysql的select并未进行加锁,导致查询出的库存信息与实际的库存数量不...
  • C++多线程多少个线程算多?

    千次阅读 2018-10-25 16:54:08
    使用多线程,为的是提高执行效率;那么,是不是线程越多越好呢? 假设我们有100个下载任务,我们可以有以下3种实现方法: 使用一个线程,依次执行100个下载任务; 使用100个线程,每个线程执行一个下载任务; ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 78,725
精华内容 31,490
热门标签
关键字:

多线程的时间上限

友情链接: Test.rar