Python多线程代码执行重复 您所在的位置:网站首页 python多线程执行同一个函数 Python多线程代码执行重复

Python多线程代码执行重复

2023-08-07 23:35| 来源: 网络整理| 查看: 265

最近在学习多线程的时候,代码书写逻辑不对,导致线程总是执行重复的操作,,记录一下自己遇到的创建线程的几种方式.

线程(thread)是进程(process)中的一个实体,一个进程至少包含一个线程。比如,对于视频播放器,显示视频用一个线程,播放音频用另一个线程。如果我们把进程看成一个容器,则线程是此容器的工作单位。

进程和线程的区别主要有:

进程之间是相互独立的,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,但互不影响;而同一个进程的多个线程是内存共享的,所有变量都由所有线程共享;

由于进程间是独立的,因此一个进程的崩溃不会影响到其他进程;而线程是包含在进程之内的,线程的崩溃就会引发进程的崩溃,继而导致同一进程内的其他线程也崩溃.

多线程

在Python中,进行多线程的编程模块有两个: thread和threadding,,前者是低级模块,后者是高级模块,经常用的是高级模块,是对thread的封装,/

 

锁机制:

由于同一个进程之间的线程是内存共享的,所以当多个线程对同一个变量进行修改的时候,就会得到意想不到的结果。

先看一个简单例子: 

# 多线程简单例子 from threading import Thread, current_thread num = 0 def calc(): global num print('thread %s is running...' % current_thread().name) # 输出线程名 for _ in range(100000): num += 1 print('thread %s end.....' % current_thread().name) if __name__ == '__main__': print('thread %s is running...' % current_thread().name) threads=[] #创建线程池 for i in range(5): threads.append(Thread(target=calc))# 添加线程 threads[i].start()#启动线程 for i in range(5): threads[i].join() print('global num: %d' % num)

在上面的代码中,我们创建了 5 个线程,每个线程对全局变量 num 进行 10000 次的 加 1 操作,这里之所以要循环 10000 次,是为了延长单个线程的执行时间,使线程执行时能出现中断切换的情况。现在问题来了,当这 5 个线程执行完毕时,全局变量的值是多少呢?是 50000 吗?不一定是:

原因是 num + 1不是一个原子操作,也就是在执行的时候是被分成若干步执行了,

由于线程是交替运行的,线程在执行过程中可能会中断,导致其他线程读到一个脏值,

为了保证计算的准确性,我们就需要给 num += 1 这个操作加上 锁 。当某个线程开始执行这个操作时,由于该线程获得了锁,因此其他线程不能同时执行该操作,只能等待,直到锁被释放,这样就可以避免修改的冲突。创建一个锁可以通过 threading.Lock() 来实现,代码如下:

# 多线程简单例子 from threading import Thread, current_thread,Lock num = 0 lock=Lock() def calc(): global num print('thread %s is running...' % current_thread().name) # 输出线程名 for _ in range(100000): lock.acquire() #加锁 num += 1 lock.release()#解锁 print('thread %s end.....' % current_thread().name) if __name__ == '__main__': print('thread %s is running...' % current_thread().name) threads=[] #创建线程池 for i in range(5): threads.append(Thread(target=calc))# 添加线程 threads[i].start()#启动线程 for i in range(5): threads[i].join() print('global num: %d' % num) Python多线程需要注意的几种情况:

1.采用创建指定进程数量来去处理项目,不采用传参和全局变量控制方式,

import time from threading import Thread, current_thread, Lock def add(): for i in range(1,255): ip_h='192.168.181.' ip=ip_h+str(i) print(ip) ips.append(ip) if __name__ == '__main__': start=time.time() ips=[] threads=[] for i in range(5): t=Thread(target=add,) threads.append(t) t.start() for i in threads: i.join() end=time.time() print("总时间"+str(start-end))

 可以看到在这种情况下,线程会重复执行,里面会有重复项出现,但执行的时间变短,但经常这种方式,不是我们想要的,因为我们不需要重复,而是直接依次的向下处理.造成这种结果的原因是,线程之间没有锁机制,然后线程之间采用并行结构,所以当线程1在处理第一个的时候,后面的都不会影响,都是从1开始处理,.

2,创建的线程是与需要的参数相关,想当于创建了254个线程,去同时执行,且在没有加锁的状态下,

import time from threading import Thread, current_thread, Lock def add(i): # for i in range(1,255): ip_h='192.168.181.' ip=ip_h+str(i) print(ip) ips.append(ip) if __name__ == '__main__': start=time.time() ips=[] threads=[] for i in range(255): t=Thread(target=add,args=(i,)) threads.append(t) t.start() for i in threads: i.join() end=time.time() print("总时间"+str(start-end))

.而这种写法,是将线程创建和参数值关联起来,每一个线程每次只会拿到一个参数值进行处理,处理结束后,整个线程 也就结束了,所以不会有重复的出现,但这种方式,处理线程并发数与参数有关,且并发量过高,如果其中有局部变量的情况下,会每次修改掉这个局部变量.对于这种情况需要采用其他方式,如:Python 提供了 ThreadLocal 对象,它真正做到了线程之间的数据隔离.

3,采用全局变量方式,对线程处理数进行限制,这样,一个线程每一次只能拿到一个值,且这个值还是进过累加的,也就不会产生重复执行的情况.

不加锁: 

#(2)创建指定线程,通过全局变量方式,控制线程不执行重复操作 import time from threading import Thread, current_thread, Lock def add(): global num #全局变量关键字 ip_h = '192.168.181.' for i in range(51): # 这里的i是用来限制单个线程的所用到的循环次数,通过 处理项目总数/线程数 算出来的 # lock.acquire() ip=ip_h+str(num) #这里是控制线程执行顺序,当下一次线程进来,也只能只能执行,下一次项目,不会再重复处理之前的值 num += 1 # lock.release() print(ip) ips.append(ip) if __name__ == '__main__': start=time.time() lock=Lock() num=1 ips=[] threads=[] for i in range(5): t=Thread(target=add,) threads.append(t) t.start() for i in threads: i.join() end=time.time() print("总时间"+str(start-end))

 加锁情况下:

 可以看到时间上还是有一定差距的.

 

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有