进程是CPU调度的基本单位,线程是进程内部进行CPU调度的基本单位,一个进程至少存在一个线程。
模块:threading
创建线程:Thread(target=func[args=(value1,value2,…)])
target:线程需要执行的任务(函数或者方法名)
args:指定线程执行的任务需要传入的实参,以元组形式传入
启动线程:start()
多线程之间是共享资源的,所以:
- 线程可以被抢占(中断)。
- 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让。
示例1:
def print_time(threadName,delay):
count =0
while count <5:
time.sleep(delay)
count +=1
print(f"{threadName}:{time.ctime(time.time())}")
if __name__ == '__main__':
th1 = threading.Thread(target=print_time,args=("th1",0.2,))
th2 = threading.Thread(target=print_time,args=("th2",0.5,))
th1.start()
th2.start()
执行结果:
示例2:
import threading #引入线程包
import time #引入时间的包
def task1(num): #任务1,例子中num--->5
for i in range(num): #循环打印task1:数字
print("task1:%d" % i)
time.sleep(0.5)
def task2(num):#任务2,例子中num--->6
for i in range(num):#循环打印task2:数字
print("task2:%d" % i)
time.sleep(0.5)
def main():#定义主函数
"""创建启动线程"""
t1 = threading.Thread(target=task1, args=(5,)) #创建一个线程,target--》分配任务(给一个函数task1)
t2 = threading.Thread(target=task2, args=(6, ))#创建一个线程,target--》分配任务(给一个函数task2)
t1.start() #t1进程启动
t2.start() #t2进程启动
t1.join() #我要等待t1全部执行完毕,才会执行后面的代码
t2.join() #我要等待t2全部执行完毕,才会执行后面的代码
if __name__ == '__main__':
main() #调用主函数,两个join保证全部线程执行完毕后,才会让主线程执行print.
print("Done!")
执行结果:
线程锁:
多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。
比如:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
示例1:使用两个线程来模拟模拟两个人使用同一个账户井发取钱操作
import threading
import time
def count_num(account,num):
# global lock
# lock.acquire() # 获得锁
if account > num: # 全局变量
print(threading.current_thread().name + "取钱成功,吐出钞票:"+ str(num))
account -= num
print("账户余额为:"+ str(account))
else:
print(threading.current_thread().name + "账户余额不足,取钱失败")
if __name__ == '__main__':
#lock = threading.Lock() # 声明了一个锁对象
# for i in range(10):
# t = threading.Thread(target=count_num,args=(1000,800))
# t.start()
th1 = threading.Thread(target=count_num,args=(1000,800))
th2 = threading.Thread(target=count_num,args=(1000,800))
th1.start()
th2.start()
print("线程执行结束")
```
多次运行,可能会出现如下错误

很明显,这并不是期望看到的结果。为了保证数据的正确性,需要对多个线程进行同步。使用线程锁可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。
示例:
```python
import threading
import time
def count_num(account,num):
global lock
lock.acquire() # 获得锁
if account > num: # 全局变量
print(threading.current_thread().name + "取钱成功,吐出钞票:"+ str(num))
account -= num
print("账户余额为:"+ str(account))
else:
print(threading.current_thread().name + "账户余额不足,取钱失败")
lock.release() #释放锁
if __name__ == '__main__':
lock = threading.Lock() # 声明了一个锁对象
# for i in range(10):
# t = threading.Thread(target=count_num,args=(1000,800))
# t.start()
th1 = threading.Thread(target=count_num,args=(1000,800))
th2 = threading.Thread(target=count_num,args=(1000,800))
th1.start()
th2.start()
print("线程执行结束")
但是数据安全是以降低程序的运行效率作为代价的,为了减少线程安全所带来的负面影响,程序可以采用如下策略:
不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源(也就是共享资源)的方法进行同步。