欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

python线程

程序员文章站 2022-06-24 11:13:38
1 import threading 2 3 def product(number): 4 print(number,) 5 6 thread_lst = [] 7 #开启100线程 8 for i in range(100): 9 t = threading.Thread(target=produ ......


"""线程"""

进程是内存分配的最小单位,线程是调度的最小单位。每个进程中至少有一个线程

"""线程与进程的区别:
进程相互独立,同一进程的各个线程间资源共享,某进程内的线程在其他进程内不可见。
进程间通信ipc,线程可以直接读写进程数据段来进行通信-需要同步和互斥锁等,保证数据的一致性。
线程上下文切换比进程快很多
在多线程操作系统中进程不是一个可执行的实体


python的线程模块常用thread、threading,threading提供更高级的线程管理建议使用。

查看treading.tread的类可知参数
def __init__(self, group=none, target=none, name=none,
args=(), kwargs=none, *, daemon=none):

target是函数
name是线程名
"""
开启线程
 1 import threading
 2 
 3 def product(number):
 4     print(number,)
 5 
 6 thread_lst = []
 7 #开启100线程
 8 for i in range(100):
 9     t = threading.thread(target=product,args=(i,))
10     t.start()   #执行start开启线程
11     thread_lst.append(t)
12 
13 for task in thread_lst:
14     task.join() #join作用是调用线程等待该线程完成后,才能继续用下运行

 

import threading

class mytread(threading.thread):
    def __init__(self,arg):
        super(mytread, self).__init__()

        self.arg = arg  #这里可以自定义传入参数

    def run(self): #run方法必须有这是要执行的函数
        print(threading.current_thread(),threading.get_ident())  #线程名和id

thread_lst = []
for i in range(100):
    t = mytread(i)
    t.start()

for task in thread_lst:
    task.join()
"""守护线程和守护进程不一样,

守护线程在子线程都执行完才结束,

守护进程当主进程代码执行完就立刻结束(主进程在代码执行完后不立刻结束而要回收资源后结束)
"""

 1 import threading
 2 
 3 def fundaemon():
 4     while true:
 5         print("守护线程")
 6 
 7 def funthread():
 8     for i in range(10):
 9         print("子线程")
10 
11 td = threading.thread(target=fundaemon)
12 td.daemon = true
13 
14 t1 = threading.thread(target=funthread())
15 td.start()
16 t1.start()
17 t1.join()
18 
19 print("主线程结束")

因为cpython解释器中的gil锁的关系,同一时间只能有一个线程访问cpu。所以python的线程实际不是并行的。

实际上看起来像并行是因为cpu时间片的轮转,每个线程给一点时间访问cpu时间到了就给的线程访问来回交替,速度太快人感觉不出来。

使用队列也可以解决时间片轮转造成的数据不安全。

为了防止线程争抢资源,或者因为时间片的轮转导致拿到的数据有误所以就有了线程锁的概念。

互斥锁
 1 from threading import lock,rlock,thread
 2 
 3 def fuc1(l):
 4     l.acquire()
 5     global num
 6     num -= 1
 7     l.release()
 8 
 9 l = lock()
10 num = 10
11 
12 for i in range(10):
13     t = thread(target=fuc1,args=(l,))
14     t.start()
15     t.join()
16 
17 print(num)

互斥锁保证只有一个线程持有锁(acquire)才能访问其中的数据并释放锁(release)后其他线程才能再次持有锁访问数据。

死锁问题:

一个线程或者进程内需要同时拿到多个锁才能处理一个问题,但是在线程争抢锁时 a线程拿到了第一个锁 b线程拿到第二个锁 那么他们都无法进行下面的处理,所以就会形成死锁。

可以理解为吃饭,a拿着饭b拿着筷子,只有同时拿着饭和筷子才能吃饭。但是a和b各拿一个就谁也吃不到。

进程内存在多个锁也会出现死锁问题

 

解决死锁问题:递归锁(rlock)

 

1 from threading import rlock
2 
3 lck = rlock()
4 
5 lck.acquire()
6 lck.acquire()
7 lck.release()
8 lck.release()

rlock可以多次acquire()和release()。拿到上一个锁才能拿到下一个锁以此类推。

 

信号量

线程的信号量看着b格很高,其实意思就是同一时间允许多少个线程访问一个数据使用方法也很简单。

 1 from threading import semaphore,thread
 2 
 3 def fun1(s):
 4     global sum
 5     s.acquire()
 6     sum += 1
 7     s.release()
 8 
 9 sum = 10
10 sp = semaphore(4) #同一时间允许4个线程访问数据
11 sp.acquire()
12 sp.release()
13 for i in range(10):
14     t = thread(target=fun1,args=(sp,))
15     t.start()
16     t.join()

当线程访问与一个信号量所关联的数据时,他必须调用accquire()操作,该操作会减少信号量内部的变量值,如果值为非负数,那么久允许访问资源,如果值为负数,那么线程就会挂起,同时等待另外一个线程释放该资源。

 

条件锁

条件锁condition实现方法:

wait()  阻塞,接收到notify信号停止阻塞

acquire() 持有锁

notify() 发送可以开启线程的信号。notify(int)可以设置钥匙数量不需要归还,类似信号量只有int个线程可以访问数据。

release() 释放锁

 1 from threading import condition,thread
 2 
 3 def fun1(con,i):
 4     con.acquire()
 5     con.wait()  #阻塞直到接收到notify信号
 6     print(i)
 7     con.release()
 8 
 9 
10 con = condition()
11 for i in range(20):
12     thread(target=fun1,args=(con,i)).start()
13 
14 num = 10
15 while num > 0:
16     num -= 1
17     con.acquire()
18     con.notify()
19     con.release()
20 
21 """开启20个线程但是运行10次notify()"""

 

事件

事件event,实现的方法:

wait() 阻塞 wait(int) 可以设置阻塞时间

is_set() 布尔值默认false

set() 设置is_set()和布尔值为true和wait()为非阻塞

事件

 

定时线程

定时开启一个线程

 1 from threading import timer
 2 import time
 3 
 4 def fun1():
 5     print("时间同步")
 6 
 7 
 8 while true:
 9     t = timer(5,fun1).start() #开启一个定时5s后运行的子线程
10     time.sleep(5) #主线程sleep 5s