Python中线程创建方式、守护线程、多进程及线程代替方案

多线程和多进程

  • 程序:一大堆代码以文本的形式存入一个文档
  • 进程:程序运行的一个状态
    • 包含地址空间、内存、数据栈等
    • 每个进程由自己完全独立的运行环境,多进程共享数据是一个问题
  • 线程
    • 一个进程的独立运行片段,一个进程可以有多个线程
    • 轻量化的进程
    • 一个进程的多个线程间共享数据和上下文运行环境
    • 共享互斥问题
  • 全局解释器锁(GIL)
    • Python代码的执行是由python虚拟机进行控制
    • 在主循环中只能有一个控制线程在执行
  • Python包
    • thread:有问题,不好用,python3改成了_thread

      • 调用thread模块中的start_new_thread()函数来产生新线程

      thread.start_new_thread ( function, args[, kwargs] )

      • 参数说明:
        • function - 线程函数
        • args - 传递给线程函数的参数,他必须是个tuple类型
        • kwargs - 可选参数
import _thread
import time

# 为线程定义一个函数
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# 创建两个线程
try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: 无法启动线程")

while 1:# 让主线程遗址等待
   pass
# 输出
Thread-1: Wed Jul 24 15:17:17 2019
Thread-2: Wed Jul 24 15:17:19 2019Thread-1: Wed Jul 24 15:17:19 2019

Thread-1: Wed Jul 24 15:17:21 2019
Thread-2: Wed Jul 24 15:17:23 2019
Thread-1: Wed Jul 24 15:17:23 2019
Thread-1: Wed Jul 24 15:17:25 2019
Thread-2: Wed Jul 24 15:17:27 2019
Thread-2: Wed Jul 24 15:17:31 2019
Thread-2: Wed Jul 24 15:17:35 2019
  • threading:通行的包
    • 直接使用threading.Thread
      • t = threading.Thread(target=xxx, args=(xxx,))
      • t.start():启动线程
      • t.join():等待多线程执行完成
#方式一
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.start()
    print('主线程')
  • 直接继承threading.Thread
    • 直接继承Thread
    • 重写run函数
    • 类实例可以直接运行
#方式二
from threading import Thread
import time
class Sayhi(Thread):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        time.sleep(2)
        print('%s say hello' % self.name)

if __name__ == '__main__':
    t = Sayhi('egon')
    t.start()
    print('主线程')

守护线程deamon

  • 无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
  • 一般认为,守护线程不允许离开主线程独立运行
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.setDaemon(True) #必须在t.start()之前设置
    t.start()

print('主线程')
print(t.is_alive())
  • 线程常用属性

    • threading.currentThread(): 返回当前的线程变量。
    • threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
    • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
    • isAlive(): 返回线程是否活动的。
    • getName(): 返回线程名。
    • setName(): 设置线程名。
  • 共享变量

    • 当多个线程同时访问一个变量的时候,会产生共享变量问题
  • 解决方法(锁,信号灯)

    • 锁:
      • 是一个标志,表示一个线程在占用一些资源
      • 使用方法
        • 上锁
        • 使用共享变量
        • 释放锁
import threading
sum = 0
loopSum = 10000
lock = threading.Lock()

def myAdd():
    global sum, loopSum
    for i in range(1, loopSum):
        # 上锁
        lock.acquire()
        sum += 1
        # 释放锁
        lock.release()

def myMinu():
    global  sum, loopSum
    for i in range(1, 10000):
        lock.acquire()
        sun -= 1
        lock.release()

if __name__ == '__main__':
    print("Starting .......{0}".format(sum))
    t1 = threading.Thread(target=myAdd, args=())
    t2 = threading.Thread(target=myMinu(), args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("Done .......{0}".format(sum))
  • 线程安全问题
    • 如果一个变量,对于多线程来讲,不加锁也不会引起任何问题,则称为线程安全
    • 线程不安全变量类型:list,set,dict
    • 线程安全变量类型:queue
  • 生产者消费者问题
    • 一个模型
    • 可以用来搭建消息队列
    • queue是一个用来存放变量的数据结构,特点是先进先出,内部元素排队
import queue
import threading
import time

class Producer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            if queue.qsize()<1000:
                for i in range(100):
                    count = count + 1
                    msg = '生成产品' + str(count)
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)

class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    msg = self.name + '消费了' + queue.get()
                    print(msg)
            time.sleep(1)

if __name__ == '__main__':
    queue = queue.Queue()
    for i in range(500):
        queue.put('初始产品' + str(i))
    for i in range(2):
        p = Producer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()
  • 死锁问题
import threading
import time
lock_1 = threading.Lock()
lock_2 = threading.Lock()

def func_1():
    print("func_1 staring..........")
    lock_1.acquire()
    print("func_1 申请了lock_1.....")
    time.sleep(2)
    print("fun_1 等待 lock_2.......")
    lock_2.acquire()
    print("fun_1 申请了 lock_2.....")

    lock_2.release()
    print("func_1 释放了 lock_2......")
    lock_1.release()
    print("func_1 释放了 lock_1")
    print("func_1 done.........")

def func_2():
    print("func_2 staring..........")
    lock_2.acquire()
    print("func_2 申请了lock_2.....")
    time.sleep(4)
    print("fun_2 等待 lock_1.......")
    lock_1.acquire()
    print("fun_2 申请了 lock_1.....")
    lock_1.release()
    print("func_2 释放了 lock_1......")
    lock_2.release()
    print("func_2 释放了 lock_2")
    print("func_2 done.........")

if __name__ == '__main__':
    print("主程序启动。。。。。。。。")
    t1 = threading.Thread(target=func_1(),args=())
    t2 = threading.Thread(target=func_2(), args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("主程序启动。。。。。。。。。。。。。。。")
  • 锁的等待时间问题
    • lock_1.acquire(timeout=4)
    • semphore
      • 允许一个资源最多由一个线程同时访问
import threading
import time
# 参数表示最多由几个线程同时使用
semphore = threading.Semaphore(3)
def func():
    if semphore.acquire():
        for i in range(5):
            print(threading.currentThread().getName() + 'get semphore')
        time.sleep(15)
        semphore.release()
        print(threading.currentThread().getName() + 'realease semphore')

for i in range(8):
    t1 = threading.Thread(target=func())
    t1.start()
  • threading.Timer
    • 利用多线程,在指定时间后启动一个功能
import threading
import time
def func():
    print("I am running.........")
    time.sleep(4)
    print("I am done .........")

if __name__ == '__main__':
    t = threading.Timer(6, func)
    t.start()
    i = 0
    while True:
        print("{0}*********************".format(i))
        time.sleep(3)
        i += 1
  • 可重入锁
    • 一个锁,可以被线程多次申请
    • 主要解决递归调用的时候,需要申请锁的情况
import threading
import time
num = 0
mutex = threading.RLock()
class MyThread(threading.Thread):
    def run(self):
        global num
        time.sleep(1)
        if mutex.acquire(1):
            num = num + 1
            msg = self.name + 'set num to ' + str(num)
            print(msg)
            mutex.acquire()
            mutex.release()
            mutex.release()

def ta():
    for i in range(5):
        t = MyThread()
        t.start()

if __name__ == '__main__':
    ta()

线程替代方案

  • subprocess
    • 完全跳过线程,使用进程
    • 是派生进程的主要替代方案
    • python2.4后引入
  • multiprocessing
    • 使用threading接口派生,使用子进程
    • 允许为多核或者多cpu派生进程,接口跟threading非常相似
    • python2.6
  • concurrent.futures
    • 新的异步执行模块
    • 任务级别的操作
    • python3.2后引入

多进程

  • 进程间通讯(InterprocessCommunication,IPC)
  • 进程之间没有任何共享状态
  • 进程的创建
    • 直接生成Process实例对象
    • 派生子类
import multiprocessing
from time import sleep, ctime
def clock(interval):
    while True:
        print("the time is %s" % ctime())
        sleep(interval)

if __name__ == '__main__':
    p = multiprocessing.Process(target=clock, args=(5, ))
    p.start()

    while True:
        print("sleeping......")
        sleep(1)
import multiprocessing
from time import sleep, ctime
class ClockProcess(multiprocessing.Process):
    # 两个比较重要的函数init、run
    def __init__(self, interval):
        super().__init__()
        self.interval = interval

    def run(self):
        while True:
            print("the time is %s" % ctime())
            sleep(self.interval)

if __name__ == '__main__':
    p = ClockProcess(3)
    p.start()
    while True:
        print("sleeping..........")
        sleep(1)
  • 在os中查看pid,ppid以及他们的关系
from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name', __name__)
    # 得到父进程id
    print('parent process:', os.getppid())
    print('process id:', os.getpid())
    
def f(name):
    info('function f')
    print('hello', name)
    
if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
  • 生产者消费者模型
    • JoinableQueue
import multiprocessing
from time import ctime

def consumer(input_q):
    print("Info consumer:", ctime())
    while True:
        # 处理项
        item = input_q.get()
        print("pull", item, "out of q")# 此处替换为有用的工作
        input_q.task_done() # 发出信号通知任务完成
    print("out of consumer:", ctime()) # 此句未执行
    
def producer(sequence, output_q):
    print("Into proceder:", ctime())
    for item in sequence:
        output_q.put(item)
        print("put", item, "into q")
    print("out of procuder:", ctime())
    
if __name__ == '__main__':
    q = multiprocessing.JoinableQueue()
    # 运行消费者进程
    cons_p = multiprocessing.Process(target=consumer, args=(q,))
    cons_p.daemon = True
    cons_p.start()
    # 生产多个项,sequeue代表要发送给消费者的序列项
    # 在实践中,这可能是生成器的输出或通过一些其他方式生产出来
    sequeue = [1,2,3,4]
    producer(sequeue. q)
    q.join()
  • 哨兵
import multiprocessing
from time import ctime

# 设置哨兵问题
def consumer(input_q):
    print("Info consumer:", ctime())
    while True:
        # 处理项
        item = input_q.get()
        if item is None:
            break
        print("pull", item, "out of q")# 此处替换为有用的工作
    print("out of consumer:", ctime()) # 此句未执行
    
def producer(sequence, output_q):
    print("Into proceder:", ctime())
    for item in sequence:
        output_q.put(item)
        print("put", item, "into q")
    print("out of procuder:", ctime())
    
if __name__ == '__main__':
    q = multiprocessing.Queue()
    # 运行消费者进程
    cons_p = multiprocessing.Process(target=consumer, args=(q,))
    cons_p.start()
    # 生产多个项,sequeue代表要发送给消费者的序列项
    # 在实践中,这可能是生成器的输出或通过一些其他方式生产出来
    sequeue = [1,2,3,4]
    producer(sequeue. q)
    q.put(None)
    cons_p.join()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值