Python多线程代码执行重复

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

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

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

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

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

多线程

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

Python多线程代码执行重复_第1张图片

 

锁机制:

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

先看一个简单例子: 

# 多线程简单例子
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 吗?不一定是:

Python多线程代码执行重复_第2张图片

原因是 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))

Python多线程代码执行重复_第3张图片

Python多线程代码执行重复_第4张图片

 可以看到在这种情况下,线程会重复执行,里面会有重复项出现,但执行的时间变短,但经常这种方式,不是我们想要的,因为我们不需要重复,而是直接依次的向下处理.造成这种结果的原因是,线程之间没有锁机制,然后线程之间采用并行结构,所以当线程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多线程代码执行重复_第5张图片

.而这种写法,是将线程创建和参数值关联起来,每一个线程每次只会拿到一个参数值进行处理,处理结束后,整个线程 也就结束了,所以不会有重复的出现,但这种方式,处理线程并发数与参数有关,且并发量过高,如果其中有局部变量的情况下,会每次修改掉这个局部变量.对于这种情况需要采用其他方式,如: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))

Python多线程代码执行重复_第6张图片Python多线程代码执行重复_第7张图片

 加锁情况下:

Python多线程代码执行重复_第8张图片Python多线程代码执行重复_第9张图片

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

 

 

 

你可能感兴趣的:(Python基础,python)