进程与线程

什么是进程?

An executing instance of a program is called a process.

Each process provides the resources needed to execute a program. A process has a virtual address space, executable code, open handles to system objects, a security context, a unique process identifier, environment variables, a priority class, minimum and maximum working set sizes, and at least one thread of execution. Each process is started with a single thread, often called the primary thread, but can create additional threads from any of its threads.

程序的执行实例称为进程。

每个进程提供执行程序所需的资源。 进程具有虚拟地址空间,可执行代码,系统对象的打开句柄,安全上下文,唯一进程标识符,环境变量,优先级类,最小和最大工作集大小以及至少一个执行线程。 每个进程都是通过单线程启动的,通常称为主线程,但是可以从其任何线程创建其他线程。

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

有了进程为什么还要线程?

进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

例如,我们在使用qq聊天, qq做为一个独立进程如果同一时间只能干一件事,那他如何实现在同一时刻 即能监听键盘输入、又能监听其它人给你发的消息、同时还能把别人发的消息显示在屏幕上呢?你会说,操作系统不是有分时么?但我的亲,分时是指在不同进程间的分时呀, 即操作系统处理一会你的qq任务,又切换到word文档任务上了,每个cpu时间片分给你的qq程序时,你的qq还是只能同时干一件事呀。

再直白一点, 一个操作系统就像是一个工厂,工厂里面有很多个生产车间,不同的车间生产不同的产品,每个车间就相当于一个进程,且你的工厂又穷,供电不足,同一时间只能给一个车间供电,为了能让所有车间都能同时生产,你的工厂的电工只能给不同的车间分时供电,但是轮到你的qq车间时,发现只有一个干活的工人,结果生产效率极低,为了解决这个问题,应该怎么办呢?。。。。没错,你肯定想到了,就是多加几个工人,让几个人工人并行工作,这每个工人,就是线程!

什么是线程?

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

A thread is an execution context, which is all the information a CPU needs to execute a stream of instructions.

Suppose you're reading a book, and you want to take a break right now, but you want to be able to come back and resume reading from the exact point where you stopped. One way to achieve that is by jotting down the page number, line number, and word number. So your execution context for reading a book is these 3 numbers.

If you have a roommate, and she's using the same technique, she can take the book while you're not using it, and resume reading from where she stopped. Then you can take it back, and resume it from where you were.

Threads work in the same way. A CPU is giving you the illusion that it's doing multiple computations at the same time. It does that by spending a bit of time on each computation. It can do that because it has an execution context for each computation. Just like you can share a book with your friend, many tasks can share a CPU.

On a more technical level, an execution context (therefore a thread) consists of the values of the CPU's registers.

Last: threads are different from processes. A thread is a context of execution, while a process is a bunch of resources associated with a computation. A process can have one or many threads.

线程不同与进程。一个线程是一个可执行的上下文,而一个进程是和计算相关联一堆资源。一个进程有一个或多个线程

Clarification: the resources associated with a process include memory pages (all the threads in a process have the same view of the memory), file descriptors (e.g., open sockets), and security credentials (e.g., the ID of the user who started the process).

与资源相关联的进程包括内存页面(在一个进程中所有线程共享同一块内存空间),文件描述符,安全凭证

进程与线程的区别?

  1. Threads share the address space of the process that created it; processes have their own address space.线程共享创建它的进程的内存空间;进程有它自己的内存空间
  2. Threads have direct access to the data segment of its process; processes have their own copy of the data segment of the parent process.线程可以直接访问其进程的数据段; 进程具有自己的父进程的数据段副本。
  3. Threads can directly communicate with other threads of its process; processes must use interprocess communication to communicate with sibling processes.同一个进程的线程之间可以直接交流,两个进程间想通信,必须通过一个中间代理来实现
  4. New threads are easily created; new processes require duplication of the parent process.创建新线程很简单,创建新进程需要对其父进程进行一次克隆
  5. Threads can exercise considerable control over threads of the same process; processes can only exercise control over child processes.一个线程可以控制和操作同一进程里的其它线程,进程只能操作子进程
  6. Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process; changes to the parent process does not affect child processes.对主线程的修改可能会影响同一进程里其它线程的行为,对父进程的影响不影响子进程

并发的多线程例子

import threading,time

def run(name, trans= 'running'):
    print("{} is {}".format(name,trans))
    time.sleep(2)

r1 = threading.Thread(target=run,args=("lxj",),kwargs = {"trans":"walking"}) #这里注意args如果只传入一个参数,后面要加逗号,因为args是元组的形式
r2 = threading.Thread(target=run,args=("sx",))
r1.start()
r2.start()

#run("lxj")
#run("sx")

#这里的运行效果是,使用线程会同时打印信息,然后等2秒结束程序。
#直接运行函数的效果是,先运行run("lxj"),再运行run("sx"),总共需4秒继承

继承式多线程写法

在子类中覆盖run()方法

import threading,time
class MyThread(threading.Thread):

    def __init__(self,n):
        super().__init__()
        self.n = n
     self.setDaemon(True) #设置守护线程,具体有什么用,下面会讲到 def run(self): print("{} running".format(self.n)) time.sleep(2) t1 = MyThread("lxj") t2 = MyThread("sx") t1.start() t2.start() #和上面多线程例子是一样的效果,只是写法不一样 

这样一个个写线程太慢了,如何一下子创建多个线程呢?这里很多人应该知道了,那就是用循环

import threading,time

def run(name, trans= 'running'):
    print("{} is {}".format(name,trans))
    time.sleep(2)
for i in range(50):
    t = threading.Thread(target=run, args=("sx{}".format(i),))
    t.start()

#这里就一下子创建了50一个线程,都用来执行run函数

这里我们可以计算下程序运行完所花的时间

import threading,time

def run(name, trans= 'running'):
    print("{} is {}".format(name,trans))
    time.sleep(2)
start_time = time.time()
for i in range(50):
    t = threading.Thread(target=run, args=("sx{}".format(i),))
    t.start()
total_time = time.time()-start_time
print("运行时间{}".format(total_time))
运行结果为:先运行时间0.01559,等2s程序运行结束。可是我们要的结果不是等2s,最后再统计时间并打印运行时间。为什么会出现这样的结果呢?

现在这个情况我们可以理解为程序本身就是主线程,创建子线程后,子线程与主线程就没有关系。创建的子线程和主线程是并行的,并不会等待子线程运行完再运行主线程,我们程序也说明了这个情况

那我们要统计该如何统计呢?

import threading,time

def run(name, trans= 'running'):
    print("{} is {}".format(name,trans))
    time.sleep(2)

t_objs = []
start_time = time.time()
for i in range(50):
    t = threading.Thread(target=run, args=("sx{}".format(i),))
    t.start()
    t_objs.append(t)
for t in t_objs:
    t.join()    #相当于wait,等待线程执行完成,这里等待50个线程全部执行完成
total_time = time.time()-start_time
print("运行时间{}".format(total_time))

#此时运行程序就是我们想要的结果了,可自行在机子上运行

  查看程序用的是子线程还是主线程

 

查看当前活动的线程数

守护线程

如果你设置一个线程为守护线程,就表示你在说这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。 
如果你的主线程在退出的时候,不用等待那些子线程完成,那就设置这些线程的daemon属性。即,在线程开始(thread.start())之前,调用setDeamon()函数,设定线程的daemon标志。(thread.setDaemon(True))就表示这个线程“不重要”。

如果你想等待子线程完成再退出,那就什么都不用做。或者显示地调用thread.setDaemon(False),设置daemon的值为false。新的子线程会继承父线程的daemon标志。整个Python会在所有的非守护线程退出后才会结束,即进程中没有非守护线程存在的时候才结束。

看代码

import threading,time

def run(name, trans= 'running'):
    print("{} is {} {}".format(name,trans,threading.current_thread()))
    time.sleep(2)

start_time = time.time()
for i in range(3):
    t = threading.Thread(target=run, args=("sx{}".format(i),))
    t.setDaemon(True)
    t.start()


total_time = time.time()-start_time
print("运行时间{} {} {}".format(total_time,threading.current_thread(),threading.active_count()))


#运行程序可以看到主线程没有等待子线程运行完就退出了

  

posted @ 2017-07-18 16:06  zj-luxj  阅读(122)  评论(0编辑  收藏  举报