Python之路(第四十一篇)线程概念、线程背景、线程特色、threading模块、开启线程的方式

 

 

1、线程

​ 以前咱们已经了解了操做系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,咱们容许多个程序同时加载到内存中,在操做系统的调度下,能够实现并发地执行。这是这样的设计,大大提升了CPU的利用率。进程的出现让每一个用户感受到本身独享CPU,所以,进程就是为了在CPU上实现多道编程而提出的。html

有了进程为何要有线程

​ 进程有不少优势,它提供了多道编程,让咱们感受咱们每一个人都拥有本身的CPU和其余资源,能够提升计算机的利用率。不少人就不理解了,既然进程这么优秀,为何还要线程呢?其实,仔细观察就会发现进程仍是有不少缺陷的,主要体如今三点上:python

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

  • 进程在执行的过程当中若是阻塞,例如等待输入,整个进程就会挂起,即便进程中有些工做不依赖于输入的数据,也将没法执行。编程

  • 进程间的数据没法直接共享服务器

线程的出现缘由

正常状况下,咱们在启动一个程序的时候。这个程序会先启动一个进程,启动以后这个进程会拉起来一个线程。这个线程再去处理事务。也就是说真正干活的是线程,进程这玩意只负责向系统要内存,要资源可是进程本身是不干活的。默认状况下只有一个进程只会拉起来一个线程。markdown

​ 多线程顾名思义,就是一样在一个进程的状况同时拉起来多个线程。上面说了,真正干活的是线程。进程与线程的关系就像是工厂和工人的关系。那么如今工厂仍是一个,可是干活的工人多了。那么效率天然就提升了。由于只有一个进程,因此多线程在提升效率的同时,并无向系统伸手要更多的内存资源。所以使用起来性价比仍是很高的。可是多线程虽然不更多的消耗内存,可是每一个线程却须要CPU的的参与。数据结构

​ 至关于工厂虽然厂房就一间,能够有不少的工人干活。可是这些工人怎么干活还得靠厂长来指挥。工人太多了,厂长忙不过来安排同样效率不高。因此工人(线程)的数量最好仍是在厂长(cpu)的能力(内核数)范围以内比较好。多线程

 

线程出现的背景

​ 60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了不少弊端,一是因为进程是资源拥有者,建立、撤消与切换存在较大的时空开销,所以须要引入轻型进程;二是因为对称多处理机(SMP)出现,能够知足多个运行单位,而多个进程并行开销过大。并发

  所以在80年代,出现了能独立运行的基本单位——线程(Threads)app

  注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.

     每个进程中至少有一个线程。 

 

线程特色

  1)轻型实体

  线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。

  线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述,

  2)独立调度和分派的基本单位。  在多线程OS中,线程是能独立运行的基本单位,于是也是独立调度和分派的基本单位。因为线程很“轻”,故线程的切换很是迅速且开销小(在同一进程中的)。 

  3)共享进程资源。  线程在同一进程中的各个线程,均可以共享该进程所拥有的资源,这首先表如今:全部线程都具备相同的进程id,这意味着,线程能够访问该进程的每个内存资源;此外,还能够访问进程所拥有的已打开文件、定时器、信号量机构等。因为同一个进程内的线程共享内存和文件,因此线程之间互相通讯没必要调用内核。

  4)可并发执行。  在一个进程中的多个线程之间,能够并发执行,甚至容许在一个进程中全部线程都能并发执行;一样,不一样进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工做的能力。

 

线程和进程的关系

 

  • 根据上图能够看出,进程包含线程。也就是说默认状况下 一个进程确定会有一个线程的,这个线程叫主线程。

  • 多线程也就是在一个进程里面开出多个线程。多进程里面也能够包含多线程。

  • 多进程之间是不能够直接通信的。可是因为多线程是被同一个进程包裹,故多线程中资源共享便可以直接通信。

  • 进程自己不可以执行

  • 进程和线程不能比较谁快谁慢,两个没有可比性,进程是资源的集合,线程是真正执行任务的,进程要执行任务也要经过线程

  • 启动一个线程比启动一个进程快,线程上下文切换比进程上下文切换要快得多

 

 

python线程模块的选择

 Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块容许程序员建立和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块容许用户建立一个能够用于多个线程之间共享数据的队列数据结构。  通常避免使用thread模块,使用更高级别的threading模块,对线程的支持更为完善,并且使用thread模块里的属性有可能会与threading出现冲突。

 

 

2、threading模块

 

multiprocess模块的彻底模仿了threading模块的接口,两者在使用层面,有很大的类似性

 

开启线程的两种方式

 

一、方式一

  
  from threading import Thread
  import time
  ​
  def func(n):
      time.sleep(2)
      print("线程是%s"%n)
      global g
      g = 0
      print(g)
  ​
  if __name__ == '__main__':
      g = 100
      t_l = []
      for i in range(10):
          t = Thread(target=func,args=(i,))
          t.start()
          t_l.append(t)
      
      for t in t_l:
          t.join()
      print("主线程G",g)
  ​

  

 

二、方式二

import threading
import time


def func(n):
    time.sleep(2)
    print("线程是%s" % n)
    print('子线程的ID号A', threading.current_thread().ident)
    global g
    g = 0
    print('子线程中的g', g)


class Mythread(threading.Thread):

    def __init__(self, arg, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.arg = arg

    def start(self):
        print('start-----')
        super().start()  # 调用父类的start()和run()方法

    def run(self):
        print("类中的子线程", self.arg)
        super().run()
        print('子线程的ID号B',threading.current_thread().ident)


if __name__ == '__main__':
    g = 100
    t1 = Mythread('hello', target=func, name="MyThread", args=('nick',))
    # 第一个参数是用在Mythread类中的,后面的3个参数用在建立的func子线程中,args必须是可迭代的
    # 这里的func也能够直接写在Mythread中的run()里,这时这里的run()不用再继承父类的run()
    t1.start()
    # t1.run()
    t1.join()
    print('主线程中的g', g)
    print('主线程的ID号---', threading.current_thread().ident)

  简化版:

import time
from threading import Thread
class MyTread(Thread):
    def __init__(self,arg):
        super().__init__()
        self.arg = arg
    def run(self):
        time.sleep(1)
        print(self.arg)
        print('直接在这里写子进程的代码')

t = MyTread('hello')
t.start()

  

  

 

多线程与多进程区别

一、运行方式不一样:

进程不能单独执行,它只是资源的集合。

进程要操做CPU,必需要先建立一个线程。

全部在同一个进程里的线程,是同享同一块进程所占的内存空间。

 

二、关系

进程中第一个线程是主线程,主线程能够建立其余线程;其余线程也能够建立线程;线程之间是平等的。

进程有父进程和子进程,独立的内存空间,惟一的标识符:pid。

 

三、速度

启动线程比启动进程快。

运行线程和运行进程速度上是同样的,没有可比性。

线程共享内存空间,进程的内存是独立的。

 

四、建立

父进程生成子进程,至关于复制一分内存空间,进程之间不能直接访问

建立新线程很简单,建立新进程须要对父进程进行一次复制。

一个线程能够控制和操做同级线程里的其余线程,可是进程只能操做子进程。

 

五、交互

同一个进程里的线程之间能够直接访问。

两个进程想通讯必须经过一个中间代理来实现。

 

 

测试下进程和线程谁开启的速度快

  
  from multiprocessing import Process
  from threading import Thread
  import time
  ​
  def func(n):
      n + 1
  ​
  ​
  if  __name__ == "__main__":
      start_t = time.time()
      t_li = []
      for i in range(100):
          t = Thread(target=func, args=(i,))
          t.start()
          t_li.append(t)
      for t in t_li: t.join()
      druing_time1 = time.time() - start_t
  ​
      start_p = time.time()
      p_li = []
      for i in range(100):
          p = Process(target=func, args=(i,))
          p.start()
          p_li.append(p)
      for p in p_li: p.join()
      druing_time2 = time.time() - start_t
      print(druing_time1,druing_time2)

  

分析:根据执行结果显示:开启线程的速度比进程的速度快上百倍以上。

 

 

看下多线程和多进程的PID

  
  from threading import Thread
  from multiprocessing import Process
  import os
  ​
  def work():
      print('函数内hello',os.getpid())
  ​
  if __name__ == '__main__':
      #part1:在主进程下开启多个线程,每一个线程都跟主进程的pid同样
      t1=Thread(target=work)
      t2=Thread(target=work)
      t1.start()
      t2.start()
      print('主线程/主进程pid',os.getpid())
  ​
      #part2:开多个进程,每一个进程都有不一样的pid
      p1=Process(target=work)
      p2=Process(target=work)
      p1.start()
      p2.start()
      print('主线程/主进程pid',os.getpid())

  

 

测试下多线程内共享数据的状况

  
  from  threading import Thread
  from multiprocessing import Process
  import os
  def work():
      global n
      n=0
  ​
  if __name__ == '__main__':
  ​
      # 多进程的状况
      # n=100
      # p=Process(target=work)
      # p.start()
      # p.join()
      # print('主',n)
      #毫无疑问子进程p已经将本身的全局的n改为了0,但改的仅仅是它本身的,查看父进程的n仍然为100
      #
      #
  ​
      # 多线程的状况
      n=1
      t=Thread(target=work)
      t.start()
      t.join()
      print('主',n) 
      #查看结果为0,由于同一进程内的线程之间共享进程内的数据,
      # 这表示线程内数据是共享的

  

 

多线程下的socket客户端和服务端

服务端

  
  import socket
  from threading import Thread
  ​
  ​
  server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  server.bind(("127.0.0.1", 8081))
  server.listen(5)
  buffer_size = 1024
  ​
  def chat(conn,addr):
  ​
      res = conn.recv(buffer_size)
      print("addr",addr,res.decode("utf-8"))
      msg = input("请输入:").strip()
      conn.send(msg.encode("utf-8"))
  ​
  def func():
  ​
      while True:
          print("服务器开始运行了")
          conn,addr = server.accept()
          res = Thread(target=chat,args=(conn,addr)).start()
  ​
  func()
 

  

客户端

  
  import socket
  ​
  buffer_size = 1024
  client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  client.connect(("127.0.0.1",8081))
  ​
  while True:
      msg = input("请输入").strip()
      client.send(msg.encode("utf-8"))
      res = client.recv(buffer_size)
      print(res.decode("utf-8"))

  

 

分析:这里可测试同时开启多个客户端与服务端聊一次天

 

 

 

 

 

参考连接

[1]https://blog.csdn.net/u013421629/article/details/79208227

[2]https://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

相关文章
相关标签/搜索