python线程

为什么由线程?1,进程的创建,撤销,切换开销比较大。2,由于对称多处理及(SMP)即多cpu的出现,进程并行开销过大。线程,轻量级进程,一个基本的cpu执行单元,程序执行过程中的最小单元,由线程id,程序计数器,寄存器集合,和堆栈共同组成。线程可以与同属于一个进程的其他线程共享进程拥有的资源。线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的数据被强制删除并退出。在pycharm上运行一段代码并不会开一个进程,所以有时需要守护线程

 

线程是轻量级进程,一个标准的线程有线程id,当前指令,寄存器集合和堆栈组成,线程是进程的实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有在运行中必不可少的资源,但它可以与同属有一个进程的其他线程共享进程所拥有的全部资源。

使用threading模块创建线程

#coding=utf-8
import threading
import time

def saySorry():
    print("亲爱的,我错了,我能吃饭了吗?")
    time.sleep(1)

if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=saySorry)
        t.start() #启动线程,即让线程开始执行

  多线程并发操作,当调用start()方法后才真正创建线程并执行,每一个线程都有一个唯一的标识符用来区分程序的主次关系,程序主入口被称为主线程,使用threading.Thread()创建的都是子线程,线程数量等于:主线程+子线程数

主线程默认会等待所有子线程结束后再结束

len(threading.enumerate())  查看线程数量

 

同时开启几个线程,是没有执行顺序的

t1 = threading.Thread(target=)

t1.setDaemon(True)  设置守护线程

多线程共享全局变量,如果多个线程同时更改同一个全局变量,会造成错误的结果,需要通过t1.join() 让其他线程等待t1线程执行结束再执行,也就相当于没有并发而是并行

 

互斥锁

murtes = threading.Lock()  使用Lock模块创建线程锁对象

mutes.acquire   锁定同一个进程下不同线程的共享资源,其他线程不能更改此资源

mutes.release()  释放锁

确保了某段关键代码只能由一个线程从头到尾完整地执行

 

死锁

在线程共享多个资源 时,如果两个线程分别占有一部分资源并同时等待对方的资源,就会造成死锁。

 

线程的建立

import threading
import time
import os
'''
theading模块建立在_thread模块基础上,thread模块以低级原始的方式处理和控制线程,threading模块对
thread模块进行二次封装,提供了更方便的api处理线程
本代码创建了20个’前台‘线程,然后控制器交给cpu,通过指定算法调度,分片执行指令,
'''

def work(num):
    print('in work{}'.format(num))
    time.sleep(2)


if __name__ == '__main__':

    for i in range(20):
        t = threading.Thread(target=work, args=(i,), name='t.{}'.format(i))  # args = 后面必须是元组, name参数设置线程名
        t.setDaemon(True)  # 在start方法之前设置默认为False,即前台线程,子线程结束后主线程再结束;为True时设置为守护线程,主线程结束,则无论子线程是否执行完毕都结束,即后台线程

        t.start()
        # t.setName('tt.i')  # 设置线程名
        # print(t.getName())  # 获取线程名
        t.name = 'ttt.{}'.format(i)  # t.name设置线程名
        print(t.name)  # t.name 获取线程名
        # print(t.is_alive())  # 判断线程是否是激活状态
        # print(t.isAlive())  # 也是判断线程是否是激活状态 后面的括号不能忘!!
        print(t.isDaemon())  # 判断是否为守护线程
        print(t.ident)  # 获取线程的标识符,在本脚本多线程运行的时候,140310960539392线程标示符不同,进程号相同
        # t.join()  # 逐个执行每个线程,执行完毕后在继续向下执行,此方法让多线程变的毫无意义,线程标识符相同,进程号相同
        print(os.getpid())  # 获取进程号,在本脚本多线程,单线程运行时进程号都相同
        # t.run() :线程被cpu调度后自动执行线程对象的run方法


# import threading
# import time
#
#
# def worker(num):
#     """
#     thread worker function
#     :return:
#     """
#     time.sleep(1)
#     print("The num is  %d" % num)
#     return
#
#
# for i in range(20):
#     t = threading.Thread(target=worker, args=(i,),name ='t. % d' % i)
#     t.start()

  

线程锁

import threading
import time
'''
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。
为了保证数据的准确性,引入了锁的概念。所以,可能出现如下问题:
例:假设列表A的所有元素就为0,当一个线程从前向后打印列表的所有元素,
另外一个线程则从后向前修改列表的元素为1,那么输出的时候,列表的元素就会一部分为0,
一部分为1,这就导致了数据的不一致。锁的出现解决了这个问题。
'''


globals_num = 0

lock = threading.RLock()


def Func():
    lock.acquire()  # 获得锁 
    global globals_num
    globals_num += 1
    time.sleep(1)
    print(globals_num)
    lock.release()  # 释放锁


for i in range(10):  # 连续创建了十个进程,由于Func方法内有锁,多线程只能一个一个运行
    t = threading.Thread(target=Func)
    t.start()

  

线程锁Lock和RLock区别

import threading
import time

'''
由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,CPU接着执行其他线程。为了保证数据的准确性,引入了锁的概念。
RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。
 如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,
 必须调用n次的release才能真正释放所占用的琐。
'''
# lock = threading.Lock()
# lock.acquire()
# lock.acquire()  # 代码在此行产生了死锁
# lock.release()
# lock.release()

# rLock = threading.RLock()
# rLock.acquire()
# rLock.acquire()
# rLock.release()
# rLock.release()







'''下面的代码都计算错误了'''


num = 0


def work(lock):
    global num
    num += 1
    time.sleep(0.5)
    # print(num)


def work1(lock):
    time.sleep(0.5)
    global num
    num += 1


if __name__ == '__main__':
    lock = threading.Lock()
    for i in range(10000):
        t = threading.Thread(target=work, args=(lock,))
        t1 = threading.Thread(target=work1, args=(lock,))

        t.start()
        t1.start()

    print(num)



import threading
import time

'''
两个线程同时连续修改同一个进程中的同一个全局变量一百万次,运行结束后全局变量被计算错误。
可以使用threading.Lock,acquire上锁,release解锁
'''


def work1(num):
    for i in range(num):
        global count
        count += 1
    print('in work1', count)


def work2(num):
    for i in range(num):
        global count
        count += 1
    print('in work2', count)


if __name__ == '__main__':
    count = 0
    num = 10000
    t1 = threading.Thread(target=work1, args=(num,))
    t2 = threading.Thread(target=work2, args=(num,))
    t1.start()
    t2.start()

    while len(threading.enumerate()) != 1:
        time.sleep(0.5)

    print(count)

  

 

线程Event

import threading

'''
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
clear:将“Flag”设置为False
set:将“Flag”设置为True
Event.isSet() :判断标识位是否为Ture。
当线程执行的时候,如果flag为False,则线程会阻塞,当flag为True的时候,线程不会阻塞。它提供了本地和远程的并发性。
'''


def do(event):
    print('do start')
    event.wait()
    print('do over')


if __name__ == '__main__':
    event_obj = threading.Event()
    for i in range(10):
        t1 = threading.Thread(target=do, args=(event_obj,))
        t1.start()
    event_obj.clear()
    in_data = input('true?')
    if in_data == 'true':
        event_obj.set()

  

自己做一个线程池

# 简单往队列中传输线程数
import threading
import time
import queue

class Threadingpool():
    def __init__(self,max_num = 10):
        self.queue = queue.Queue(max_num)
        for i in range(max_num):
            self.queue.put(threading.Thread)

    def getthreading(self):
        return self.queue.get()

    def addthreading(self):
        self.queue.put(threading.Thread)


def func(p,i):
    time.sleep(1)
    print(i)
    p.addthreading()


if __name__ == "__main__":
    p = Threadingpool()
    for i in range(20):
        thread = p.getthreading()
        t = thread(target = func, args = (p,i))
        t.start()

  

#往队列中无限添加任务
import queue
import threading
import contextlib
import time

StopEvent = object()


class ThreadPool(object):

    def __init__(self, max_num):
        self.q = queue.Queue()
        self.max_num = max_num

        self.terminal = False
        self.generate_list = []
        self.free_list = []

    def run(self, func, args, callback=None):
        """
        线程池执行一个任务
        :param func: 任务函数
        :param args: 任务函数所需参数
        :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
        :return: 如果线程池已经终止,则返回True否则None
        """

        if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
            self.generate_thread()
        w = (func, args, callback,)
        self.q.put(w)

    def generate_thread(self):
        """
        创建一个线程
        """
        t = threading.Thread(target=self.call)
        t.start()

    def call(self):
        """
        循环去获取任务函数并执行任务函数
        """
        current_thread = threading.currentThread
        self.generate_list.append(current_thread)

        event = self.q.get()  # 获取线程
        while event != StopEvent:   # 判断获取的线程数不等于全局变量

            func, arguments, callback = event   # 拆分元祖,获得执行函数,参数,回调函数
            try:
                result = func(*arguments)   # 执行函数
                status = True
            except Exception as e:    # 函数执行失败
                status = False
                result = e

            if callback is not None:
                try:
                    callback(status, result)
                except Exception as e:
                    pass

            # self.free_list.append(current_thread)
            # event = self.q.get()
            # self.free_list.remove(current_thread)
            with self.work_state():
                event = self.q.get()
        else:
            self.generate_list.remove(current_thread)

    def close(self):
        """
        关闭线程,给传输全局非元祖的变量来进行关闭
        :return:
        """
        for i in range(len(self.generate_list)):
            self.q.put(StopEvent)

    def terminate(self):
        """
        突然关闭线程
        :return:
        """
        self.terminal = True
        while self.generate_list:
            self.q.put(StopEvent)
        self.q.empty()

    @contextlib.contextmanager
    def work_state(self):
        self.free_list.append(threading.currentThread)
        try:
            yield
        finally:
            self.free_list.remove(threading.currentThread)




def work(i):
    print(i)
    return i +1 # 返回给回调函数

def callback(ret):
    print(ret)

pool = ThreadPool(10)
for item in range(50):
    pool.run(func=work, args=(item,),callback=callback)

pool.terminate()
# pool.close()

  

posted @ 2018-07-22 23:24  贾祥飞  阅读(244)  评论(0编辑  收藏  举报