python——进程与线程

一、线程与进程的基础

概念:

1.进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。即CPU资源分配时是以进程为单位进行分配,线程在执行时会使用CPU资源。
2.进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统上进行资源分配和调度的一个独立单位。
3.线程是进程的一个实体,是CPU调度和分派的基本单位,一个进程可以拥有多个线程。同个进程内的多个线程共享全部资源,同一个进程内的多个线程之间可以并发执行,线程拥有自己的栈空间,拥有独立的执行序列。
4.区别:进程有独立的地址空间,一个进程崩溃后,不会对其它进程产生影响,而线程只是一个进程 中的不同执行路径,线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉。

线程的其它:

1.线程的生命周期:创建、可执行、未可执行、消亡
2.线程的同步:多个线程同时操作一个对象时,应该保持对象数据的统一性和整体性,避免出现脏读。

死锁产生条件(且关系):

1.互斥条件:线程使用的资源必须至少有一个是不能共享的,即任意时刻一个资源只能给一个线程使用。
2.请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其他线程持有的资源。
3.非剥夺条件:分配的资源不能从相应的线程中被强制剥夺。
4.循环等待条件:第一个线程等待其他线程,后者又在等待第一个线程。

多线程的优点:

1.使用线程可以把占据长时间的程序中的任务放到后台去处理。
2.用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
3.程序的运行速度可能加快
4.在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

二、python中多线程的使用

Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,一般都选择使用threading模块。
Python中使用线程有两种方式:函数或者用类来包装线程对象。

函数方式-创建一个线程对象:
def write(name):
	pass
thread4 = threading.Thread(target=write(),args=(name,))		#创建一个线程对象
thread4.start()		#启动线程

类包装方式:
class myThread(threading.Thread):	#继承父类threading.Thread
	    def __init__(self, thread_name, delay_time,count):
        threading.Thread.__init__(self)		#继承父类时要先执行父类的初始化语句
        self.thread_name = thread_name
        self.delay_time = delay_time
        self.count = count
        self.s = 0
        
	 #必要!必须要重构run方法,把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
    def run(self):
        print("start:" + self.thread_name)
        get_count(self.thread_name,self.delay_time,self.count)
        other(self.thread_name,self.count)
        write()
        read(self.thread_name)
        print("end:" + self.thread_name)
	
thread1 = myThread("thread1",3,3)	#创建线程对象
thread2 = myThread("thread2",2,4)	#创建线程对象
thread1.start()		#启动
thread2.start()		#启动Thread2

threading 模块提供的其他方法:

threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

线程模块同样提供了Thread类(Thread对象)来处理线程,Thread类提供了以下方法:
run(): 用以表示线程活动的方法。
start():启动线程活动。
join([time]): 等待至线程中止。这阻塞调用线程直至线程的join() 方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。

线程同步Lock:

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

解决方法:给操作的方法加一把锁,上锁时仅当前线程能使用,其它线程不可访问继续等待,直到当前线程释放锁之后,其它线程再执行该语句模块。

代码实现

lock = threading.Lock()   
lock.acquire()	#获得锁
#代码语句
lock.release()	#释放锁

注:获得锁之后必须要释放锁,不然会让其它线程一直等待形成死锁,可以使用try…finally确保最后能释放锁

完整代码实现:

'''
@author: hanqingqing
@date: 2020/04/04
@content: threading
'''
import threading
import time

#封装线程对象
class myThread(threading.Thread):
    def __init__(self, thread_name, delay_time,count):
        threading.Thread.__init__(self)
        self.thread_name = thread_name
        self.delay_time = delay_time
        self.count = count
        self.s = 0

    def run(self):
        print("start:" + self.thread_name)
        get_count(self.thread_name,self.delay_time,self.count)
        other(self.thread_name,self.count)
        write()
        read(self.thread_name)
        print("end:" + self.thread_name)

s = 0
t = 0
def get_count(thread_name, delay_time,count):
    global s    #引入全局变量
    while count:
        s += 1
        print(thread_name + ': '+ str(s))
        time.sleep(delay_time)
        count -= 1
def other(name , count):
    while count:
        print(name+'另一个: ' + str(count))
        time.sleep(count)
        count -= 1
def write():
    global t
    lock.acquire()  #互斥锁,锁定变量,如果当前有进程正在执行该模块,其它进程等待
    for i in range(3):
        t += 1
        time.sleep(2)
        t -= 1
    lock.release()  #释放锁
def read(name):
    global t
    print(name + 't为:' + str(t))

lock = threading.Lock()
thread1 = myThread("thread1",3,3)
thread2 = myThread("thread2",2,4)
thread3 = myThread("thread3",2,5)
thread1.start()
#thread1.join()      #join()可以等当前进程运行完成后,后面的进程再进行
thread2.start()
thread3.start()
thread4 = threading.Thread(target=write(),args=(name,))

三、ThreadLocal

多线程环境下,每一个线程均可以使用所属进程的全局变量。如果一个线程对全局变量进行了修改,将会影响到其他所有的线程。为了避免多个线程同时对变量进行修改,引入了线程同步机制,通过互斥锁,条件变量或者读写锁来控制对全局变量的访问。

只用全局变量并不能满足多线程环境的需求,很多时候线程还需要拥有自己的私有数据,这些数据对于其他线程来说不可见。因此线程中也可以使用局部变量,局部变量只有线程自身可以访问,同一个进程下的其他线程不可访问。

有时候使用局部变量不太方便,因此 python 还提供了 ThreadLocal 变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的。

要想单个线程都有自己的私有数据,并可对其作特有的更改,可以通过形参传参实现,但这种方法太过于繁琐,而且这样耦合度过高,方式不可取。
例:

import threading


def process_2(x,n):
    x = x + n
    print(threading.current_thread().name+"为:"+str(x))

def process_thread(x,n):
    process_2(x,n)

thread1 = threading.Thread(target=process_thread,args=(3,4),name="Thread1")
thread2 = threading.Thread(target=process_thread,args=(4,8),name="Thread2")
thread2.start()
thread1.start()

ThreadLocal实现:

import threading
num = 0
local_number = threading.local()

def process_student():
    number = local_number.std
    print(str(number)+"在"+threading.current_thread().name)
def process_thread(x ,number):
    local_number.std = number   #绑定ThreadLocal的std
    local_number.x = x      #绑定ThreadLocal的x
    local_number.num = num  #绑定ThreadLocal的num,仅当前线程可访问
    process_student()
    process_1()
def process_1():
    local_number.num = local_number.num + local_number.std+local_number.x   #可直接获取当前线程ThreadLocal的x和std值
    print(threading.current_thread().name+"计算为"+str(local_number.num))

thread1 = threading.Thread(target=process_thread,args=(3,2),name="Thread1")
#thread1 = threading.Thread(target=process_thread(3),name="Thread1")    #这样执行是在主线程执行,而不是子进程
thread2 = threading.Thread(target=process_thread,args=(4,7),name="Thread2")
thread1.start()
thread2.start()

输出:

2在Thread1
Thread1计算为5
7在Thread2
Thread2计算为11

可见,两个线程的计算互不影响,又免去了繁琐的传参操作。ThreadLocal 真正做到了线程之间的数据隔离,并且使用时不需要手动获取自己的线程 ID。ThreadLocal就相当于帮我们生成一个全局的dict,存储key为特定线程ID,value为私有数据,要使用时又能自动获取对应的值。

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

Queue

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

Queue模块中的常用方法:

Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值