鱼C论坛

 找回密码
 立即注册
查看: 2251|回复: 5

[技术交流] 多线程教程发个,花了一下午时间研究了。。。

[复制链接]
发表于 2018-3-24 18:42:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 风琪仙 于 2018-3-24 18:46 编辑

Threading模块
对象
Thread
表示一个执行线程的对象
Lock
锁原语
RLock
单一线程全局解锁
Condition
条件变量对象,使得一个线程等待另一个线程满足特定的“条件”,比如改变状态或某个数据值
Event
条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活
Semaphore
为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞
BoundedSemaphore
与 Semaphore 相似,不过它不允许超过初始值
Timer
与 Thread 相似,不过它要在运行前等待一段时间
Barrier①
创建一个“障碍”,必须达到指定数量的线程后才可以继续
Thread 对象数据属性
name
线程名
ident
线程的ID
daemon
布尔标志,表示这个线程是否是守护线程
Thread 对象方法
_init_(group=None, tatget=None, name=None, args=(),
kwargs ={}, verbose=None, daemon=None) ③
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 线程名;
args/kwargs: 要传入方法的参数。
属 性
描 述
start()
开始执行该线程(等待调度)
run()
定义线程如何执行(通常在子类中被应用开发者重写)
join (timeout=None)
直至启动的线程终止之前一直挂起;除非给出了 timeout(超时:秒),否则会一直阻塞后面代码运行
getName()①
返回线程名
setName (name)①
设定线程名
isAlivel /is_alive ()②
布尔标志,表示这个线程是否还存活
isDaemon()③
如果是守护线程,则返回 True;否则,返回 False
setDaemon(daemonic)③
把线程的守护标志设定为布尔值 daemonic(必须在线程 start()之前调用)
1、如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止
2、如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止。但如果使用了join将打破这个限制。
activeCount/ active_count()①
当前活动的 Thread 对象个数
current Thread() /current_thread①
返回当前的 Thread 对象
enumerate()
返回当前活动的 Thread 对象列表
settrace (func) ②
为所有线程设置一个 trace 函数
setprofile (func) ②
为所有线程设置一个 profile 函数
stack_size (size=0) ③
返回新创建线程的栈大小;或为后续创建的线程设定栈的大小为 size
Threading两种启动线程方式
import threading
'''
threading.Thread 默认的构造函数要求传入   函数名+参数
通过子类化threading.Thread 类。重写构造函数__init__  重写
运行函数run 实现自定义的线程操作
'''


#创建子类继承自 threading.Thread
class myThread (threading.Thread):
    def __init__(self, threadID, threadname):#覆盖threading.Thread构造函数
        threading.Thread.__init__(self)
        self.threadID = threadID          #赋值ID(参数)
        self.name = threadname            #赋值线程名(参数)

    def run(self):#覆盖threading.Thread  run方法,threading.Thread类会在start中调用 run
        func(self.name,self.threadID)      #运行func

def func(threadName, threadID):
        print("线程名:%s 线程ID%d"%(threadName,threadID))


# 创建新线程
thread1 = myThread(1, "Thread-1")
thread2 = myThread(2, "Thread-2")

# 开启新线程
thread1.start()
thread2.start()
#等待线程完成
thread1.join()
thread2.join()
print ("__________________________________2个线程都完成了")


'''
直接使用threading.Thread
'''
def fu(a,b):
    print(a+b)

t=threading.Thread(target=fu,args=(2,1))
t.start()
t.join()
资源锁同步资源访问
只负责数据同步,线程执行顺序跟我没关系!线程说:可能我刚执行完,我又获得了资源锁。
import threading
from time import sleep



def fn(name):
    global const
    for i in range(1,10000,1):
        #suo.acquire()
        const+=1
        const-=1
        #suo.release()
        print(name,const)

const=1
suo=threading.RLock()   #锁只能锁只能锁共享资源,而不能挂起别的线程。
t1=threading.Thread(target=fn,args=("线程1",))
t2=threading.Thread(target=fn,args=("线程2",))
t1.start()
t2.start()
print("主线程停止,但子线程还在运行,因为子线程不是后台线程")
Rlock与lock
import threading
'''
RLockLock区别在于,RLock在同一线程内多次连续使用acquire不会(死锁)
注意:每次调用acquire都必须成对调用一次release,内部维护了一个计数器。
'''

suo=threading.RLock()
#suo=threading.Lock()
suo.acquire()
suo.acquire()
print("<<<<<<<<")
suo.release()
suo.release()
print(">>>>>>>>")
Condition
控制线程执行顺序,也可以实现资源同步。
import threading
'''
Condition类更加类似Windows中的互斥量,这种方式直接是调度线程方式。
使得线程以一种推拉的方式工作。在此模式中访问共享资源的因果关系
更加明确!比如:先有产品生产,再有产品销售。

使用资源锁类RLock\Lock 主要实现资源的快速访问,比如某些线程将数据放到队列
又有某些线程从队列取出数据处理,并从队列删除。它们没有特定的因果关系,(异步关系)
Condition类更加注重有因果关系的数据访问。必须一个过程完成后才能进行下一个过程。
'''


def fn(name):
    for i in range(1,1000,1):
        global const
        if con.acquire():      #获取锁(同一时间只能有一个线程获取锁)
            const += 1
            print(name, const)

            con.notify()            # 通知其他线程,(你们可以做好获取锁的准备,我要释放了~
            #con.notifyAll()        # 通知所有其他线程(你们可以做好获取锁的准备,我要释放了~
            con.wait()              #释放锁,并把自己再次放到线程池,等待通知



const=0
con = threading.Condition()     #共享锁
t1=threading.Thread(target=fn,args=("线程1",))
t2=threading.Thread(target=fn,args=("线程2",))
t1.start()
t2.start()
Event类
异步中的异步
import threading
from time import sleep
'''
Event类用于异步的异步调度。比如下载文件分段。多个文件下载时,调度线程使用事件管理下面
的子线程。而此调度线程又属于另外一个主线程的子线程。因为在下载文件时不仅仅只下载一个文件
每个下载任务都是可控的,比如:暂停,开始.

多线程中,被调度后,环境都是保存的(注意)
'''


def fn(name):
    s=0
    for i in range(1,1000,1):
        global const
        if ev.wait():             #等待通知
            const += 1
            s+=1
            print(name, const,s)



const=0
ev = threading.Event()             #创建事件
t1=threading.Thread(target=fn,args=("线程1",))
t2=threading.Thread(target=fn,args=("线程2",))
t1.start()
t2.start()
ev.set()                           #设置标志为真,通知所有线程运行。
#ev.isSet()                        #是否设置了标志。当内置标志为True时返回True
ev.clear()                        #清除标志。所有使用该事件的线程会立马停止!
ev.set()
sleep(0.0001)
#ev.clear()

print("End")                      #注意由于不是后台线程,主线程结束后,子线程还在运行。除非调用clear或子线程自然结束。
Timer
延迟执行
import threading
from time import sleep
'''
Time类,定时器,Windows定时器不同,这里的Timer是延迟执行。
'''

def fn():
    for i in range(1,1000,1):
        global const
        const += 1
        print(const)

const=0
time=threading.Timer(1,fn)#设置延迟时间,要调用的函数
time.start()#开始执行。
验证多线程运行同一个函数,局部变量问题
import threading
from time import sleep

def fn2(name,index,v):
    s=[1,2,3,4,5,6]
    print("线程:%s开始执行!并完成s的赋值"%(name,))
    suo.acquire()  #获取锁
    s[index]=v
    print(name, s)
    sleep(5)       #延时,模拟代码复杂度。
    suo.release()  #释放锁

suo=threading.RLock()
print("___________________多线程运行同一函数,函数内变量是【可变类型】")
t1=threading.Thread(target=fn2,args=("线程1",0,6666))
t2=threading.Thread(target=fn2,args=("线程2",1,9999))
t1.start()
t2.start()
'''
输出:
线程1 [6666, 2, 3, 4, 5, 6]
线程2 [1, 9999, 3, 4, 5, 6]
'''

#结论:线程在运行同一个函数时,函数内部局部变量都是独立的! 线程间互相不影响
线程局部类local
import threading
'''
对于同一个local,线程无法访问其他线程设置的属性;线程设置的属性不会被其他线程设置的同名属性替换。
总结:给你要个全局量,你们各玩各的。一般这个量是要个通用的属性量。线程内可以进行设置。
函数内的的局部量也是各玩各的。如果是全局量就要指定这个。
'''
local = threading.local()
local.tname = 1


def func():
    for i in range(1,10,1):
        local.tname+=2      #local.tname+=2 错误?只能赋值常数?
        print("子线程输出:%d"%(local.tname))

t1 = threading.Thread(target=func)
t1.start()
t1.join()

print("——————主线程输出:%d"%(local.tname))

'''
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
子线程输出:2
——————主线程输出:1
'''
Barrier
多线程等待同步执行!
import threading
from time import sleep
'''
应用:多线程下载一个文件时,由于每个线程下载不同的片段,可能有些线程下载速度
较快,提前完成了,而有的线程下载较慢,还在下载,Barrier可以等待所有线程下载
完成后,整合成一个完整的文件。

0wait:线程已经就位。超时参数:timeout  比喻:有女嘉宾等急了,都到了这么久了还
有人没来,已经到我的底线了,我要搞点事情。超时会进入broken状态,如果栅栏 在处于
broke状态的时候调用reset函数会抛出一个BrokenBarrierError异常。


1reset:设置为初始状态 所有已经在等待的线程会接收到BrokenBarrierError异常
(如果没填满,其他线程就死锁了,需要异常处理)比喻:由于某个女嘉宾原因,节目
组场地换了,而其他女嘉宾还在去原先节目组场地,有些的已经到节目组场地了,这时
节目组打电话(异常)通知女嘉宾们换场地了(女嘉宾们重新去新的目的地)。

2abort:设置为打破状态。所有已经在等待的线程 都会接收到BrokenBarrierError异常
比喻:由于白衣光头少年的光大亮节,女嘉宾们生气了,节目搞不下去了。其中一个或
多个女嘉宾闹事了。(台子被掀翻了)节目搞不下去咯。而其他女嘉宾还不知道,节目组只能
通知(异常)所有女嘉宾们,今天就到这里吧。


【节目组(主线程)的监视器】属性
n_waiting:正在等待本 barrier 的线程的数量(女嘉宾来了几个人了?)
broken:栅栏是否为broken状态,返回一个布尔值(女嘉宾是否闹事了?)

【节目组:和谐最重要,没事尽量不要搞事情^_^
'''


def fn(name,time):
    for i in range(1,10,1):
        sleep(time)
        print(name,i)
        ba.wait()       #每一次调用对"栅栏"进行一次填空。


def ting():             #线程数量到达设定数量(填满了),栅栏自动复原并调用这个监听函数。
    print("光头白衣少年:下一批~")


print("主持人:3位靓丽女嘉宾靓丽登场。你对谁最有感觉?")
t1=threading.Thread(target=fn,args=("线程1执行:",0.1))    #创建线程,并给它们设置不同延时,模拟代码复杂度的不同
t2=threading.Thread(target=fn,args=("线程2执行:",0.2))
t3=threading.Thread(target=fn,args=("线程3执行:",0.3))



ba=threading.Barrier(parties=3,action=ting)  #多对象(线程)等待。只有当全部线程都运行结束时才返回
t1.start()#启动所有线程(女嘉宾登场了)
t2.start()
t3.start()

#由于节目组要求,主办方不能提前退场(等待所有子线程结束)
t1.join()
t2.join()
t3.join()
print("主办方:泪牛满面。。。")
Semaphore信号量
控制最大线程执行量!
import threading
from time import sleep
'''
应用:当程序限制运行任务数时非常有用,可以控制,最多有多少个线程同时运行。
比如写要一个服务器,同一时间有多个请求到来,不可能来一个请求,就处理一个请求
如果这样,其他人的请求就收不到了。这时可以通过主线程创建会话线程放到队列,再使用
信号量对象,根据服务器当前状态,设置并行处理数量。
'''

def fn(name):
    for i in range(1,10,1):
        sp.acquire()  #占用
        print(name, "占坑闭关")
        sleep(0.5)
        sp.release()  #释放

t1=threading.Thread(target=fn,args=("线程1执行:",))    #创建线程
t2=threading.Thread(target=fn,args=("线程2执行:",))
t3=threading.Thread(target=fn,args=("线程3执行:",))

sp=threading.Semaphore(2)          #创建信号量(好,这里有个厕所,只有2个坑)

t1.start()#启动所有线程(3个人要WC)
t2.start()
t3.start()

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-4-17 14:20:47 | 显示全部楼层
必须给赞,很详细很不错
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-22 22:25:39 | 显示全部楼层
谢谢楼主,感谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-22 23:02:33 From FishC Mobile | 显示全部楼层
感觉好有用,谢谢楼主
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-24 11:28:17 | 显示全部楼层
6666
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-6-24 20:36:53 | 显示全部楼层
非常感谢分享
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-28 18:11

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表