zoukankan      html  css  js  c++  java
  • 并发编程(一)

    程序运行分两部分:

    1、I/O操作部分,此时CPU处于空闲状态

    2、计算部分,此时CPU处于工作状态

    很多情况下程序中I/O操作相当费时,这就造成了CPU使用率低下,那能不能在这个空闲时间去执行别的程序?这就出现了多道程序设计技术

    在当前作业执行I/O操作时,切换另一个程序使用CPU,提高CPU使用率。

    这里有一个关键词:切换

        既然是切换,那么这就涉及到了状态的保存,状态的恢复,加上程序A与程序B所需要的系统资
        源(内存,硬盘,键盘等等)是不一样的。自然而然的就需要有一个东西去记录程序A和程序B
        分别需要什么资源,怎样去识别程序A和程序B等等,所以就有了一个叫进程的抽象

    进程定义

    进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。

    线程

    线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,
    使到进程内并发成为可能。
    
    假设,一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。若只有
    一个进程,势必造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。若有多
    个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的
    任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协作涉及到了进程通信问题,而且
    有共同都需要拥有的东西-------文本内容,不停的切换造成性能上的损失。若有一种机制,可以使
    任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带
    来的性能损耗,那就好了。是的,这种机制就是线程。
    
    线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序
    计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发
    性能。线程没有自己的系统资源。

    线程与进程的关系

    1 一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器)
    
    2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    
    3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和
      程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 
    
    4 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调
      度的一个独立单位. 
      线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程
    自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是
    它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

    python的GIL

    python的GIL机制使得同一时刻只能运行一个线程,无法享受多核的同一时刻多线程运行的好处,也就是说在python里多线程主要解决I/O操作切换程序,提高单核CPU效率

    不能真正实现同一时刻多个线程并行。

    python的线程与threading模块

        threading 模块建立在thread 模块之上。thread模块以低级、原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装,

    提供了更方便的api来处理线程。

    直接调用:

     
    import threading
    import time
     
    def sayhi(num): #定义每个线程要运行的函数
     
        print("running on number:%s" %num)
     
        time.sleep(3)
     
    if __name__ == '__main__':
     
        t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例
        t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例
     
        t1.start() #启动线程
        t2.start() #启动另一个线程
     
        print(t1.getName()) #获取线程名
        print(t2.getName())
     

    继承式调用:

     
     
     
    import threading
    import time
    
    
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
    
        def run(self):#定义每个线程要运行的函数
    
            print("running on number:%s" %self.num)
    
            time.sleep(3)
    
    if __name__ == '__main__':
    
        t1 = MyThread(1)
        t2 = MyThread(2)
        t1.start()
        t2.start()
        
        print("ending......")

    threading模块的join和setDaemon方法

    import threading
    import time
    
    
    class MyThread(threading.Thread):
        def __init__(self,num):
            threading.Thread.__init__(self)
            self.num = num
    
        def run(self):#定义每个线程要运行的函数
    
            print("running on number:%s" %self.num)
    
            time.sleep(3)
    
    if __name__ == '__main__':
    
        t1 = MyThread(1)
        t2 = MyThread(2)
       t2.setdaemon(True) # 声明:t2为守护线程,主线程执行完后不等待t2直接结束,必须在start() 方法调用之前设置 t1.start() t2.start() t1.join() # 声明:在t1执行完后主线程才能结束
    print("ending......") # 主线程会等待子线程执行完后再结束

    join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

    setDaemon(True):

             将线程声明为守护线程,必须在start() 方法调用之前设置, 如果不设置为守护线程程序会被无限挂起。这个方法基本和join是相反的。

             当我们 在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成

             想退出时,会检验子线程是否完成。如 果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是 只要主线程

             完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法啦

    其它方法

    # run():  线程被cpu调度后自动执行线程对象的run方法
    # start():启动线程活动。
    # isAlive(): 返回线程是否活动的。
    # getName(): 返回线程名。
    # setName(): 设置线程名。
    
    threading模块提供的一些方法:
    # threading.currentThread(): 返回当前的线程变量。
    # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

    同步锁
    在多个线程调用同一操作时,若线程切换时间小于操作运行时间会出现线程共享一个资源,造成资源破坏
    我们可以通过同步锁来解决这种问题
    R=threading.Lock()
     
    ####
    def sub():
        global num
        R.acquire()
        temp=num-1
        time.sleep(0.1)
        num=temp
        R.release()
    
    

    线程死锁和递归锁 

    在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。

     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 import threading
     4 import time
     5 
     6 
     7 class MyThread(threading.Thread):
     8 
     9     def actionA(self):
    10         A.acquire()  # count=1
    11         print(self.name, "gotA", time.ctime())
    12         time.sleep(2)
    13         B.acquire()  # count=2
    14 
    15         print(self.name, "gotB", time.ctime())
    16         time.sleep(1)
    17 
    18         A.release()  # count=1
    19         B.release()  # count=0
    20 
    21     def actionB(self):
    22         B.acquire()
    23         print(self.name, "gotB", time.ctime())
    24         time.sleep(2)
    25 
    26         A.acquire()
    27         print(self.name, "gotA", time.ctime())
    28         time.sleep(1)
    29 
    30         A.release()
    31         B.release()
    32 
    33     def run(self):
    34         self.actionA()
    35         self.actionB()
    36 
    37 
    38 if __name__ == '__main__':
    39 
    40     A=threading.Lock()
    41     B=threading.Lock()
    42 
    43     # r_lcok = threading.RLock()
    44     L = []
    45 
    46     for i in range(5):
    47         t = MyThread()
    48         t.start()
    49         L.append(t)
    50 
    51     for i in L:
    52         i.join()
    53 
    54     print("ending....")

    解决办法:使用递归锁,

    lockA=threading.Lock()

    lockB=threading.Lock()<br>#--------------<br>lock=threading.RLock()
    
    

    为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

     1 import  threading
     2 import time
     3 
     4 
     5 class MyThread(threading.Thread):
     6 
     7     def actionA(self):
     8 
     9         r_lcok.acquire() #count=1
    10         print(self.name,"gotA",time.ctime())
    11         time.sleep(2)
    12         r_lcok.acquire() #count=2
    13 
    14         print(self.name, "gotB", time.ctime())
    15         time.sleep(1)
    16 
    17         r_lcok.release() #count=1
    18         r_lcok.release() #count=0
    19 
    20 
    21     def actionB(self):
    22 
    23         r_lcok.acquire()
    24         print(self.name, "gotB", time.ctime())
    25         time.sleep(2)
    26 
    27         r_lcok.acquire()
    28         print(self.name, "gotA", time.ctime())
    29         time.sleep(1)
    30 
    31         r_lcok.release()
    32         r_lcok.release()
    33 
    34 
    35     def run(self):
    36 
    37         self.actionA()
    38         self.actionB()
    39 
    40 
    41 if __name__ == '__main__':
    42 
    43     # A=threading.Lock()
    44     # B=threading.Lock()
    45 
    46     r_lcok=threading.RLock()
    47     L=[]
    48 
    49     for i in range(5):
    50         t=MyThread()
    51         t.start()
    52         L.append(t)
    53 
    54 
    55     for i in L:
    56         i.join()
    57 
    58     print("ending....")





  • 相关阅读:
    python3字典删除元素和添加元素的几种方法
    查看ef core 3.1/3.0/2.1.2生成的sql语句
    C#Qrcode生成二维码支持带标题
    .net RabbitMQ 介绍、安装、运行
    Golang之初探
    MySql5.7多实例配置教程
    Centos7 安装MySql 5.7
    sqlserver智能提示插件-sql prompt(9.4.6)的安装及注册流程
    ABP框架(asp.net core 2.X+Vue)模板项目学习之路(二)--切换MySql数据库
    ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一)
  • 原文地址:https://www.cnblogs.com/roygood/p/9774077.html
Copyright © 2011-2022 走看看