精华内容
下载资源
问答
  • 并发进程之间制约关系

    千次阅读 2013-09-13 23:14:36
    并发进程之间的竞争关系为进程互斥,并发进程之间的协作关系体现为进程同步。 进程同步:指两个以上进程基于某个条件来协调他们的活动。 同步可以分为:1对称同步:事件间相互制约  2.非对称同步 进程互斥:指...

    在多道程序系统环境中,由于资源共享与进程合作,使得并发执行的进程之间可能产生相互制约关系,这些制约关系可分为两类:竞争与协作。并发进程之间的竞争关系为进程互斥,并发进程之间的协作关系体现为进程同步。

    进程同步:指两个以上进程基于某个条件来协调他们的活动。

    同步可以分为:1对称同步:事件间相互制约

                             2.非对称同步

    进程互斥:指若干进程都要使用同一资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占有资源的进程释放该资源。

    展开全文
  • 进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。 程序是含有指令和数据的...

    线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

    程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。

    进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。

    线程 是 进程 划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。从另一角度来说,进程属于操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一程序内几乎同时执行一个以上的程序段。

    线程上下文的切换比进程上下文切换要快很多

    • 进程切换时,涉及到当前进程的CPU环境的保存和新被调度运行进程的CPU环境的设置。
    • 线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。

    2. 线程有哪些基本状态?这些状态是如何定义的?

    1. 新建(new):新创建了一个线程对象。
    2. 可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取cpu的使用权。
    3. 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。
    4. 阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有 机会再次获得cpu timeslice转到运行(running)状态。阻塞的情况分三种:
    • (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waiting queue)中。
    • (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步 锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    • (三). 其他阻塞: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
    1. 死亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

    备注: 可以用早起坐地铁来比喻这个过程(下面参考自牛客网某位同学的回答):

    1. 还没起床:sleeping
    2. 起床收拾好了,随时可以坐地铁出发:Runnable
    3. 等地铁来:Waiting
    4. 地铁来了,但要排队上地铁:I/O阻塞
    5. 上了地铁,发现暂时没座位:synchronized阻塞
    6. 地铁上找到座位:Running
    7. 到达目的地:Dead

    3. 何为多线程?

    多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多核CPU的话,因为每个CPU有自己的运算器,所以在多个CPU中可以同时运行。

    4. 为什么多线程是必要的?

    1. 使用线程可以把占据长时间的程序中的任务放到后台去处理。
    2. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
    3. 程序的运行速度可能加快。

    5 使用多线程常见的三种方式

    ①继承Thread类

    MyThread.java

    public class MyThread extends Thread {
    	@Override
    	public void run() {
    		super.run();
    		System.out.println("MyThread");
    	}
    }

    Run.java

    public class Run {
    
    	public static void main(String[] args) {
    		MyThread mythread = new MyThread();
    		mythread.start();
    		System.out.println("运行结束");
    	}
    
    }
    

    运行结果: 结果 从上面的运行结果可以看出:线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。

    ②实现Runnable接口

    推荐实现Runnable接口方式开发多线程,因为Java单继承但是可以实现多个接口。

    MyRunnable.java

    public class MyRunnable implements Runnable {
    	@Override
    	public void run() {
    		System.out.println("MyRunnable");
    	}
    }

    Run.java

    public class Run {
    
    	public static void main(String[] args) {
    		Runnable runnable=new MyRunnable();
    		Thread thread=new Thread(runnable);
    		thread.start();
    		System.out.println("运行结束!");
    	}
    
    }

    运行结果: 运行结果

    ③使用线程池

    在《阿里巴巴Java开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显示创建线程。

    为什么呢?

    使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

    另外《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

    Executors 返回线程池对象的弊端如下:

    • FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
    • CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

    对于线程池感兴趣的可以查看我的这篇文章:《Java多线程学习(八)线程池与Executor 框架》 点击阅读原文即可查看到该文章的最新版。

    6 线程的优先级

    每个线程都具有各自的优先级,线程的优先级可以在程序中表明该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这个并不意味着低 优先级的线程得不到运行,而只是它运行的几率比较小,如垃圾回收机制线程的优先级就比较低。所以很多垃圾得不到及时的回收处理。

    线程优先级具有继承特性。 比如A线程启动B线程,则B线程的优先级和A是一样的。

    线程优先级具有随机性。 也就是说线程优先级高的不一定每一次都先执行完。

    Thread类中包含的成员变量代表了线程的某些优先级。如Thread.MIN_PRIORITY(常数1)Thread.NORM_PRIORITY(常数5)Thread.MAX_PRIORITY(常数10)。其中每个线程的优先级都在Thread.MIN_PRIORITY(常数1) 到Thread.MAX_PRIORITY(常数10) 之间,在默认情况下优先级都是Thread.NORM_PRIORITY(常数5)

    学过操作系统这门课程的话,我们可以发现多线程优先级或多或少借鉴了操作系统对进程的管理。

    7 Java多线程分类

    用户线程

    运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程

    守护线程

    运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “佣人”

    • 特点: 一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
    • 应用: 数据库连接池中的检测线程,JVM虚拟机启动后的检测线程
    • 最常见的守护线程: 垃圾回收线程

    如何设置守护线程?

    可以通过调用 Thead 类的 setDaemon(true) 方法设置当前的线程为守护线程。

    注意事项:

    1.  setDaemon(true)必须在start()方法前执行,否则会抛出IllegalThreadStateException异常
    2. 在守护线程中产生的新线程也是守护线程
    3. 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
    

    8 sleep()方法和wait()方法简单对比

    • 两者最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁 。
    • 两者都可以暂停线程的执行。
    • Wait通常被用于线程间交互/通信,sleep通常被用于暂停执行。
    • wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒。

    9 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

    这是另一个非常经典的java多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!

    new一个Thread,线程进入了新建状态;调用start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start()会执行线程的相应准备工作,然后自动执行run()方法的内容,这是真正的多线程工作。 而直接执行run()方法,会把run方法当成一个mian线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

    总结: 调用start方法方可启动线程并使线程进入就绪状态,而run方法只是thread的一个普通方法调用,还是在主线程里执行。

    展开全文
  • 线程与进程之间关系和区别

    千次阅读 2016-07-20 15:56:01
    线程共享的环境包括:进程代码段,进程的公有数据(利用这些数据,线程很容易实现相互间的通讯),进程打开的文件描述符,信号的处理器进程的当前目录和进程用户ID与进程组ID。 进程拥有这许多共性的同时,还拥有...

    线程共享的环境包括:进程代码段,进程的公有数据(利用这些数据,线程很容易实现相互间的通讯),进程打开的文件描述符,信号的处理器进程的当前目录和进程用户ID与进程组ID。


    进程拥有这许多共性的同时,还拥有自己的个性。有了这些个性,线程才能实现并发性。这些个性包括:
    1.线程ID。2.寄存器组的值。3.线程的堆栈(堆栈是保证线程独立运行所必需的)。4.错误码的返回值。5.线程的信号屏蔽码(但所有的线程都共享同样的信号处理器)。6.线程的优先级

    在一个进程的线程共享堆区,而进程中的线程各自维持自己堆栈。
    在 windows 等平台上,不同线程缺省使用同一个堆,所以用 C 的 malloc (或者 windows 的 GlobalAlloc)分配内存的时候是使用了同步保护的。如果没有同步保护,在两个线程同时执行内存操作的时候会产生竞争条件,可能导致堆内内存管理 混乱。比如两个线程分配了统一块内存地址,空闲链表指针错误等。 

      Symbian 的线程一般使用独立的堆空间。这样每个线程可以直接在自己的堆里分配和释放,可以减少同步所引入的开销。当线程退出的时候,系统直接回收线程的堆空间,线程内没有释放的内存空间也不会造成进程内的内存泄漏。 

      但是两个线程使用共用堆的时候,就必须用 critical section 或者 mutex 进行同步保护。否则程序崩溃时早晚的事。如果你的线程需要在共用堆上无规则的分配和释放任何数量和类型的对象,可以定制一个自己的 allcator,在 allocator 内部使用同步保护。线程直接使用这个 allocator 分配内存就可以了。这相当于实现自己的 malloc,free。但是更建议你重新审查一下自己的系统,因为这种情况大多数是不必要的。经过良好的设计,线程的本地堆应该能够满足大多数对象的需 求。如果有某一类对象需要在共享堆上创建和共享,这种需求是比较合理的,可以在这个类的 new 和 delete 上实现共享保护。 

    进程是资源分配的基本单位,线程是系统调度的基本单位。
    平时我们写的程序都是作为线程运行的;进程可以看做是包括一系列线程和资源的统称;一个进程至少包括一个
    线程(主线程,进入main函数时产生的);在其中可以创建其它线程,也可以不创建。
    同一进程间的线程究竟共享哪些资源呢,而又各自独享哪些资源呢?
    共享的资源有
    a. 堆  由于堆是在进程空间中开辟出来的,所以它是理所当然地被共享的;因此new出来的都是共享的(16位平台上分全局堆和局部堆,局部堆是独享的)
    b. 全局变量 它是与具体某一函数无关的,所以也与特定线程无关;因此也是共享的
    c. 静态变量 虽然对于局部变量来说,它在代码中是“放”在某一函数中的,但是其存放位置和全局变量一样,存于堆中开辟的.bss和.data段,是共享的
    d. 文件等公用资源  这个是共享的,使用这些公共资源的线程必须同步。Win32 提供了几种同步资源的方式,包括信号、临界区、事件和互斥体。
    独享的资源有
    a. 栈 栈是独享的
    b. 寄存器  这个可能会误解,因为电脑的寄存器是物理的,每个线程去取值难道不一样吗?其实线程里存放的是副本,包括程序计数器PC

    进程的三种状态及转换

        进程在运行中不断地改变其运行状态。通常,一个运行进程必须具有以下三种基本状态。

     就绪(Ready)状态

        当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。

     执行(Running)状态
    当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。

     阻塞(Blocked)状态
    正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。
    引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等

                                                      
    1:就绪->执行, 当前运行进程阻塞,调度程序选一个优先权最高的进程占有处理机;
    2:执行->就绪, 当前运行进程时间片用完;
    3:执行->阻塞,当前运行进程等待键盘输入,进入了睡眠状态。
    4:阻塞->就绪,I/O操作完成,被中断处理程序唤醒。


    进程的执行状态分为:核心态和用户态,两者的主要区别就是在于进程能否获取计算机的所有资源(核心态可以,用户态则受到限制)。凡是涉及到计算机根本运行的事情都应该在内核态下执行,而中断、时钟日期、存储映象图都属于系统级的资源,对这些资源的修改则都必须在核心态,但是读取则没有强制要求。

    进程的组成:进程有PCB(进程控制块)、有关程序段、和该程序段对其进行操作的数据结构集组成。

    创建进程的必须步骤:
    1.申请空白PCB(进程控制块)
    2.为新进程分派资源
    3.初始化PCB
    4.将新进程插入就绪队列


    进程于线程的主要区别:
    进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一 个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程 序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

    线程和进程的区别联系:
    1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
    2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
    两者都可以提高程序的并发度,提高程序运行效率和响应时间。
    线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
    根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:
    1、速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。
    2、资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
    3、同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内
    展开全文
  • python 多进程并发与多线程并发总结

    千次阅读 2017-05-22 18:41:27
    概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足之处在于程序与各进程之间的通信和数据共享不方便;多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在...

    本文对Python支持的几种并发方式进行简单的总结。

    Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及)。概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足之处在于程序与各进程之间的通信和数据共享不方便;多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。Python对多线程和多进程的支持都比一般编程语言更高级,最小化了需要我们完成的工作。

    一.多进程并发

    Mark Summerfield指出,对于计算密集型程序,多进程并发优于多线程并发。计算密集型程序指的程序的运行时间大部分消耗在CPU的运算处理过程,而硬盘和内存的读写消耗的时间很短;相对地,IO密集型程序指的则是程序的运行时间大部分消耗在硬盘和内存的读写上,CPU的运算时间很短。

    对于多进程并发,python支持两种实现方式,一种是采用进程安全的数据结构:multiprocessing.JoinableQueue,这种数据结构自己管理“加锁”的过程,程序员无需担心“死锁”的问题;python还提供了一种更为优雅而高级的实现方式:采用进程池。下面一一介绍。

    1.队列实现——使用multiprocessing.JoinableQueue

    multiprocessing是python标准库中支持多进程并发的模块,我们这里采用multiprocessing中的数据结构:JoinableQueue,它本质上仍是一个FIFO的队列,它与一般队列(如queue中的Queue)的区别在于它是多进程安全的,这意味着我们不用担心它的互斥和死锁问题。JoinableQueue主要可以用来存放执行的任务和收集任务的执行结果。举例来看(以下皆省去导入包的过程):

    def read(q):
        while True:
            try:
                value = q.get()
                print('Get %s from queue.' % value)
                time.sleep(random.random())
            finally:
                q.task_done()
    
    def main():
        q = multiprocessing.JoinableQueue()
        pw1 = multiprocessing.Process(target=read, args=(q,))
        pw2 = multiprocessing.Process(target=read, args=(q,))
        pw1.daemon = True
        pw2.daemon = True
        pw1.start()
        pw2.start()
        for c in [chr(ord('A')+i) for i in range(26)]:
            q.put(c)
        try:
            q.join()
        except KeyboardInterrupt:
            print("stopped by hand")
    
    if __name__ == '__main__':
        main()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    对于windows系统的多进程并发,程序文件里必须含有“入口函数”(如main函数),且结尾处必须调用入口点。例如以if __name__ == '__main__': main()结尾。

    在这个最简单的多进程并发例子里,我们用多进程实现将26个字母打印出来。首先定义一个存放任务的JoinableQueue对象,然后实例化两个Process对象(每个对象对应一个子进程),实例化Process对象需要传送target和args参数,target是实现每个任务工作中的具体函数,args是target函数的参数。

    pw1.daemon = True
    pw2.daemon = True
    • 1
    • 2
    • 1
    • 2

    这两句话将子进程设置为守护进程——主进程结束后随之结束。

    pw1.start()
    pw2.start()
    • 1
    • 2
    • 1
    • 2

    一旦运行到这两句话,子进程就开始独立于父进程运行了,它会在单独的进程里调用target引用的函数——在这里即read函数,它是一个死循环,将参数q中的数一一读取并打印出来。

    value = q.get()
    • 1
    • 1

    这是多进程并发的要点,q是一个JoinableQueue对象,支持get方法读取第一个元素,如果q中没有元素,进程就会阻塞,直至q中被存入新元素。

    因此执行完pw1.start() pw2.start()这两句话后,子进程虽然开始运行了,但很快就堵塞住。

    for c in [chr(ord('A')+i) for i in range(26)]:
            q.put(c)
    • 1
    • 2
    • 1
    • 2

    将26个字母依次放入JoinableQueue对象中,这时候两个子进程不再阻塞,开始真正地执行任务。两个子进程都用value = q.get()来读取数据,它们都在修改q对象,而我们并不用担心同步问题,这就是multiProcessing.Joinable数据结构的优势所在——它是多进程安全的,它会自动处理“加锁”的过程。

    try:
            q.join()
    • 1
    • 2
    • 1
    • 2

    q.join()方法会查询q中的数据是否已读完——这里指的就是任务是否执行完,如果没有,程序会阻塞住等待q中数据读完才开始继续执行(可以用Ctrl+C强制停止)。

    对Windows系统,调用任务管理器应该可以看到有多个子进程在运行。

    2.进程池实现——使用concurrent.futures.ProcessPoolExecutor

    Python还支持一种更为优雅的多进程并发方式,直接看例子:

    def read(q):
            print('Get %s from queue.' % q)
            time.sleep(random.random())
    
    def main():    # 这两种方式都行
        futures = set()    with concurrent.futures.ProcessPoolExecutor() as executor:
            for q in (chr(ord('A')+i) for i in range(26)):
                future = executor.submit(read, q)
                futures.add(future)    这两种方式都行    with concurrent.futures.ProcessPoolExecutor() as executor:        futures={executor.submit(read, q):q for q in range(10)}  
        try:
            for future in concurrent.futures.as_completed(futures):
                err = future.exception()
                if err is not None:
                    raise err
        except KeyboardInterrupt:
            print("stopped by hand")
    
    if __name__ == '__main__':
        main()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这里我们采用concurrent.futures.ProcessPoolExecutor对象,可以把它想象成一个进程池,子进程往里“填”。我们通过submit方法实例一个Future对象,然后把这里Future对象都填到池——futures里,这里futures是一个set对象。只要进程池里有future,就会开始执行任务。这里的read函数更为简单——只是把一个字符打印并休眠一会而已。

    try:
            for future in concurrent.futures.as_completed(futures):
    • 1
    • 2
    • 1
    • 2

    这是等待所有子进程都执行完毕。子进程执行过程中可能抛出异常,err = future.exception()可以收集这些异常,便于后期处理。

    可以看出用Future对象处理多进程并发更为简洁,无论是target函数的编写、子进程的启动等等,future对象还可以向使用者汇报其状态,也可以汇报执行结果或执行时的异常。

    二.多线程并发

    对于IO密集型程序,多线程并发可能要优于多进程并发。因为对于网络通信等IO密集型任务来说,决定程序效率的主要是网络延迟,这时候是使用进程还是线程就没有太大关系了。

    1.队列实现——使用queue.Queue

    程序与多进程基本一致,只是这里我们不必使用multiProcessing.JoinableQueue对象了,一般的队列(来自queue.Queue)就可以满足要求:

    def read(q):
        while True:
            try:
                value = q.get()
                print('Get %s from queue.' % value)
                time.sleep(random.random())
            finally:
                q.task_done()
    
    def main():
        q = queue.Queue()
        pw1 = threading.Thread(target=read, args=(q,))
        pw2 = threading.Thread(target=read, args=(q,))
        pw1.daemon = True
        pw2.daemon = True
        pw1.start()
        pw2.start()
        for c in [chr(ord('A')+i) for i in range(26)]:
            q.put(c)
        try:
            q.join()
        except KeyboardInterrupt:
            print("stopped by hand")
    
    if __name__ == '__main__':
        main()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    并且这里我们实例化的是Thread对象,而不是Process对象,程序的其余部分看起来与多进程并没有什么两样。

    2. 线程池实现——使用concurrent.futures.ThreadPoolExecutor

    直接看例子:

    def read(q):
            print('Get %s from queue.' % q)
            time.sleep(random.random())
    
    def main():
        futures = set()
        with concurrent.futures.ThreadPoolExecutor(multiprocessing.cpu_count()*4) as executor:
            for q in (chr(ord('A')+i) for i in range(26)):
                future = executor.submit(read, q)
                futures.add(future)
        try:
            for future in concurrent.futures.as_completed(futures):
                err = future.exception()
                if err is not None:
                    raise err
        except KeyboardInterrupt:
            print("stopped by hand")
    
    if __name__ == '__main__':
        main()
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    用ThreadPoolExecutor与用ProcessPoolExecutor看起来没什么区别,只是改了一下签名而已。

    不难看出,不管是使用队列还是使用进/线程池,从多进程转化到多线程是十分容易的——仅仅是修改了几个签名而已。当然内部机制完全不同,只是python的封装非常好,使我们可以不用关心这些细节,这正是python优雅之处。

    参考文献: 
    [1]. Summerfield M. Python 3 程序开发指南[J]. 2011. 
    [2]. Summerfield M, Python编程实战[J]. 2013.

    展开全文
  • 并发编程】线程和进程关系

    千次阅读 2014-10-11 12:01:06
    二、线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以...
  • 进程并发 操作系统

    2018-09-05 23:10:49
    进程并发是指:再多道程序系统中,由于内存中可以同时装入多个程序,...间断性:由于共享系统资源,以及为完成同一项任务而相互合作,致使在这些并发执行的程序之间形成了相互制约的关系。 失去封闭性:资源共享...
  • ... 当面临这些问题的时候,有两个关键词无法绕开,那就是并行和... 2、进程是资源分配的基本单位(调度单位)。  3、一个进程可以包括多个线程。  4、在单CPU计算机中,有一个资源是无法被多个程序并行使用的:...
  • 进程线程和并行并发

    千次阅读 2019-01-17 16:37:40
    定义 进程 进程是具有一定独立功能的程序,关于某个数据集合上的一次运行...进程同时又是一个可独立调度和分派的基本单位,一个进程要是能独立运行,它还必须是一个可独立调度和分派的基本单位。 线程 线程是进程...
  • 进程、线程和CPU 之间关系(一)

    千次阅读 2020-10-31 15:43:15
    今天开始,这个专题主要讲解 java 并发编程系列,从操作系统的进程、CPU、线程等底层开始,到java 并发工具类,线程、线程池,并发容器、CAS、AQS等原理、源码和使用均...今天讲进程、线程和CPU 之间关系 。 1、 ...
  • 基于Linux的Socket编程之TCP全双工Server-Client聊天程序 一、多进程并发分析:特点: 占用资源大,但是一个进程挂了不会影响另一个。这与多线程刚好相反,多线程服务器不稳定。分析: 父进程循环accept,当父...
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。 进程和线程的关系: (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 (2)资源分配...
  • Linux 进程基本概念 什么是进程

    千次阅读 2019-03-15 20:20:04
    操作系统,简称OS,是一个基本的程序集合,用来维护计算机基本的运行。操作系统主要由内核(进程管理、内存管理、文件管理、驱动管理)和其他程序(如函数库、shell等组成)。OS的目的是为了让计算机与硬件交互,...
  • 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,...
  • 什么是程序,进程和线程?三者之间有何关系?

    万次阅读 多人点赞 2017-05-10 19:44:11
    进程进程与线程的历史我们都知道计算机是由硬件和软件组成的。硬件中的CPU是计算机的核心,它承担计算机的所有任务。 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配、任务的调度。 ...
  • 文章目录cpu架构和工作原理多核cpu和多cpu架构cpu的缓存进程和线程进程和线程在多核cpu,多cpu中的运行关系 cpu架构和工作原理 计算机有5大基本组成部分,运算器,控制器,存储器,输入和输出。运算器和控制器封装到...
  • 先了解并行和并发的概念 并行:多CPU下的程序才可以并行 ... 2、进程是资源分配的基本单位(调度单位)。  3、一个进程可以包括多个线程。  4、在单CPU计算机中,有一个资源是无法被多个程...
  • 并发:拥有处理多个任务的能力,不一定要同时,不同代码块交替执行的性能,可以串行处理也可以并行处理并行:同时处理多个任务的能力,不同代码块同时执行的性能串行:指多个任务时,各个任务按顺序执行,完成一个...
  • 进程,线程,协程之间关系

    千次阅读 2018-06-07 13:32:03
    进程、线程和协程是三个在多任务处理中常听到的概念,三者各有区别又相互联系。 进程 进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”,它是CPU资源分配和调度的独立单位。  ...
  • 进程同步机制-----为进程并发执行保驾护航

    千次阅读 多人点赞 2019-11-07 10:11:52
    本文是对进程同步机制的一个大总结(9000+字吐血总结),涵盖面非常的全,包括了进程同步的一些概念、软件同步...同样我们也知道,在OS引入了进程后,可以使系统中的多道程序可以并发的执行,进程并发执行一方面极...
  • python支持的几种并发...python并发编程(多线程, 多进程, 线程池, 进程池)讲解 本文对python支持的几种并发方式进行简单的总结。(并发编程中, 还包括协程, 但是本文不做讨论) Python支持的并发分为多线程并发...
  • 并发编程面试题(2020最新版)

    万次阅读 多人点赞 2020-03-14 17:28:01
    文章目录基础知识并发编程的优缺点为什么要使用并发编程(并发编程的优点)并发编程有什么缺点并发编程三要素是什么?在 Java 程序中怎么保证多线程的运行安全?并行和并发有什么区别?什么是多线程,多线程的优劣?...
  • 程序:由代码编译成功得到的二进制文件。在Windows中就是.exe文件。程序只占用磁盘空间。 进程:运行起来的程序。进程需要占用系统资源...进程状态:进程基本状态有5种。分别为初始态、就绪态、运行态、挂起态...
  • 进程和线程关系及区别

    万次阅读 多人点赞 2015-03-15 21:06:12
    线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的...
  • 进程 Vs 线程、并发 Vs 并行

    千次阅读 2019-12-03 23:25:18
    + 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。 + 系统运行一个程序即是一个进程从创建,运行到消亡的过程。
  • 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位....同一个进程中的多个线程之间可以并发执行. 线程的引入
  • 本文详细介绍了Java并发编程基础,包括进程、线程、上下文切换、线程调度、线程优先级等常见的基本概念。
  • 进程(Process)和线程(thread):并发编程的两个基本单元。进程:(1)和同一个机器上的其他进程是彼此隔离的。(2)拥有私有的内存空间,运行时不能共享变量。(3)通过IPC(pipe/socket)进行通信。线程:(1)...
  • 文章目录1 进程同步2 信号量1 整型信号量2 记录型信号量3 AND型信号量4 信号量实现...进程之间共同完成一项任务直接发生相互作用的关系。 临界资源与临界区 临界资源指打印机,磁带机,表格 。一次只允许一个进程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 103,964
精华内容 41,585
关键字:

并发进程之间的基本关系是