zoukankan      html  css  js  c++  java
  • day 7-6 多线程及开启方式

    一. 什么是线程  

      线程:顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程

      所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。

     多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。(一个进程里面开多个线程(共享同一个进程里面的内存空间))

          例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。

    注意:

    1.所以进程里面真正干活的是线程(进程里面有线程)

    2.进程只是用来把资源互相隔离开,而线程才是真正负责cpu来调动他的

    二. 线程的创建开销小 

    创建进程的开销要远大于线程?

    如果我们的软件是一个工厂,该工厂有多条流水线,流水线工作需要电源,电源只有一个即cpu(单核cpu)

    一个车间就是一个进程,一个车间至少一条流水线(一个进程至少一个线程)

    创建一个进程,就是创建一个车间(申请空间,在该空间内建至少一条流水线)

    而建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小

    三.  线程与进程的区别 

      1.创建线程比进程开销小(开一个进程,里面就有空间了,而线程在进程里面,就没必要在开一个空间了)
      2.多线程一定是在一个进程里面开启的,共享进程里面的资源
      3.线程启动的速度快
      4.同一进程下的多个线程共享进程的资源,而多个进程之间内存空间是隔离的
      如果开进程n是相互独立的,而线程是共享了资源,就不隔离了

      在wins下开进程,子进程不会拷贝父进程的
      在linux下开进程,子进程会完全拷贝父进程的

      5.线程可以跟它所在的进程之内 的线程通信  

    四. 为何要用到多线程 

      多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:

      1. 多线程共享一个进程的地址空间

      2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用

      3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。

      4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

    五. 开启线程的方式

     1 from threading import Thread
     2 import time
     3 import random
     4 
     5 def task(name):
     6     print("%s is running"%name)
     7     time.sleep(random.randint(1,3))
     8     print("%s is done"%name)
     9 if __name__ == '__main__':
    10     t1 = Thread(target=task,args=("线程",))
    11     t1.start()
    12     print("主进程....")
    开线程方式一
     1 from threading import Thread
     2 import time
     3 import random
     4 
     5 class Mythread(Thread):
     6     def __init__(self,name):
     7         super().__init__()
     8         self.name = name
     9 
    10     def run(self):
    11         print("%s is running"%self.name)
    12         time.sleep(random.randint(1,3))
    13         print("%s is done"%self.name)
    14 
    15 
    16 if __name__ == '__main__':
    17     t1 =Mythread("子线程")
    18     t1.start()
    19     print("主线程")
    开线程方式二(类方法)

    注意:主进程开启后,默认会有一个线程,当运行t1子线程的时候,进程内是有2个线程的.

     六. 线程和进程的开启区别

    注意:线程间是平等的,并无子线程的概念.

     1 #1. 线程的开启速度要比进程快
     2 
     3 from multiprocessing import Process
     4 from threading import Thread
     5 import time
     6 
     7 def task(name):
     8     print("%s is running" %name)
     9     time.sleep(2)
    10     print("%s is done" % name)
    11 
    12 if __name__ == '__main__':
    13     # p=Process(target=task,args=("进程1",))  
    14     # p.start()             
    15     # print("主进程")          # 进程开启速度慢,开销大,所以向系统发出开进程的请求后,并不会立刻开启一个进程,才会先输出 "主进程",然后才能看到进程1的运行
    16     t = Thread(target=task,args=("线程1",))
    17     t.start()
    18     print("主进程")            # 线程开启速度快,开销小.发送开线程请求后,立刻就能开启.在线程sleep的时候,才运行"主进程"
    线程和进程的开启开销
     1 # 2 线程和进程的数据不同
     2 from multiprocessing import Process
     3 from threading import Thread
     4 import time
     5 import os
     6 n=50
     7 def task(name):
     8     global n
     9     n = 100
    10     print("task",n)
    11 
    12 
    13 if __name__ == '__main__':
    14     p=Process(target=task,args=("进程1",))
    15     p.start()
    16     print("主进程",n)          # 进程间的数据是隔离的,所以主进程还是50,进程p更改了n=100,所以task的n等于100
    17     # t = Thread(target=task,args=("线程1",))
    18     # t.start()
    19     # print("主线程",n)            # 线程之间的数据是共享的.线程t开启后,更改了n的值,因为数据是共享的,所以主线程的n也是100
    数据共享方式不同
     1 #3. pid不同
     2 
     3 from multiprocessing import Process
     4 from threading import Thread
     5 import time
     6 import os
     7 def task(name):
     8     # print(os.getpid())
     9     print(os.getpid(),os.getppid())     # 查看当前进程和父进程的pid
    10 
    11 
    12 if __name__ == '__main__':
    13     p=Process(target=task,args=("进程1",))
    14     p.start()
    15     print("主进程",os.getpid())                #2个进程,有2个不同的pid
    16     # t = Thread(target=task,args=("线程1",))
    17     # t.start()
    18     # print("主线程",os.getpid())
    Pid不同

     七. 线程的其他方法和属性

     1 from threading import Thread,currentThread,active_count,enumerate
     2 import time
     3 
     4 def task():
     5     print("%s is running"%currentThread().getName())        #获取当前线程的名字
     6     time.sleep(2)
     7     print("%s is done"%currentThread().getName())
     8 
     9 
    10 if __name__ == '__main__':
    11     t = Thread(target=task,name="线程1")
    12     t.start()
    13     t.setName("更改后的线程名")        # 输入到控制台的时候,我们会发现,running和done的名字不一样.是因为线程start后,就立刻开启了,用的名字还是"线程1",在线程1sleep
    14     #的时候,setname才完成更改,所以后面的done线程名和之前的不一样
    15     t.join()        # 等待线程1执行完毕
    16     print(t.is_alive())     # 查看线程是否存活,2种方法
    17     print(t.isAlive())
    18     currentThread().setName("我的主线程")        # 更改主线程的名字
    19     print("主线程:%s"%currentThread().getName())
    20     print(active_count())       #查看线程数量
    21     print(enumerate())          # 查看具体的线程对象,active_count方法就是len(enumerate())的结果

     八. 多线程互斥锁

      我们知道线程之间的数据是共享的,如果同时开启多个线程对同一个数据进行修改,那么发生什么样的事情呢?

      

     1 from threading import Thread,Lock
     2 import time
     3 
     4 
     5 n =100
     6 def task():
     7     global n
     8     temp = n
     9     time.sleep(0.1)
    10     n = temp-1
    11 
    12 
    13 
    14 if __name__ == '__main__':
    15     temp_li=[]
    16     for i in range(3):
    17         t1 = Thread(target=task)
    18         t1.start()
    19         temp_li.append(t1)
    20 
    21     for t in temp_li:
    22         t.join()
    23 
    24     print("main",n)
    25 -----------------------------------------------------------------------------------------
    26   main 99

      最后输出的是99.为什么不是97呢?我们循环三次,开启3个线,当第一个线程运行到sleep的时候,其他线程也都运行到sleep这里了.这时候3个线程拿到的n的值都是100,所以等sleep结束后,每个线程都做了一次100-1的操作.所以最后的n就等于99了.可是这不是我们想要的结果,我们想要的是:每运行一次,n就减去1.这个时候就应该用到互斥锁了.

    from threading import Thread,Lock
    import time
    
    
    n =100
    def task(mutex):
        global n
        mutex.acquire()
        temp = n
        time.sleep(0.1)
        n = temp-1
        mutex.release()
    
    
    
    if __name__ == '__main__':
        mutex = Lock()
        temp_li=[]
        for i in range(3):
            t1 = Thread(target=task,args=(mutex,))
            t1.start()
            temp_li.append(t1)
    
        for t in temp_li:
            t.join()
    
        print("main",n)
    -----------------------------------------------------------------------------------------
        main 97

      加上互斥锁之后,最后的n就成了97.不加互斥锁的程序运行效率比加了锁的要高.但是数据不安全,因为不加锁是并发执行的,加锁后,变成了串行执行,效率低.数据安全.

  • 相关阅读:
    1. 马尔科夫决策过程
    梯度下降法、随机梯度下降法、小批量梯度下降法
    计算曲线与坐标轴的面积
    鼠标进入,显示div
    codewar
    Django firstDay
    C#学习之关于lock
    winfrom 界面中表格数据修改及刷新(DEV)
    SVN 分支合并 版本控制
    wpf 绑定非元素对象
  • 原文地址:https://www.cnblogs.com/lovepy3/p/9278482.html
Copyright © 2011-2022 走看看